Source code for project_composer.manifest.manifesto

import json
from pathlib import Path

import tomli

from ..exceptions import ComposerManifestError

from .base import BaseConfig
from .fields import CharField, ListField, PluginField, BooleanField
from .plugins import RequirementsConfig


[docs]class Manifest(BaseConfig): """ The manifest model. Manifest fields are given as keyword arguments. Fields are: name (string) The manifest title name. collection (list) A list of application module names for enabled application. repository (string) A Python path where to search for enabled application modules. syspaths (list) A list of Path object to load in sys.path by Composer. requirements (dict) A dictionnary of items to load in ``RequirementsConfig`` for specific requirements composer. In fact this is used by ``TextContentComposer`` but requirements is actually its unique implementation. Attributes: name (string): The manifest title name. collection (list): A list of application module names for enabled application. requirements (dict or RequirementsConfig): Requirements specific options. Either as RequirementsConfig object or a dict of values respecting the RequirementsConfig attributes. """ # Payload fields declaration _FIELDS = [ CharField("name", required=True), ListField("collection", required=True), CharField("repository", required=True), CharField("default_store_app"), BooleanField("no_ordering"), ListField("syspaths"), PluginField("requirements", plugin=RequirementsConfig), ]
[docs] @classmethod def load(cls, source): """ Loading a manifest source. Arguments: cls (class): Manifest class. source (string or pathlib.Path or dict or Manifest): The Manifest source to load. It can be either: * A Manifest object, it will just be returned as it without any validation, you are responsible of its correctness; * A string for the file path to load in JSON or TOML format; * A Path object to the file to load in JSON or TOML format; * A Dictionnary which respect the manifest structure; Source file format are guessed from their file extension such as JSON for ``.json`` or TOML for ``.toml``. Return: Manifest: A Manifest model instance. """ if isinstance(source, cls): # That should commonly not happen but if a Manifest object is given, just # use it directly and bypass everything else. return source elif isinstance(source, dict): # A dict is directly used as content content = source else: # Enforce Path object source = Path(source) # JSON format is the simpliest structure which fit exactly to the manifest # structure if source.name.endswith(".json"): content = json.loads(source.read_text()) # TOML format is more complex than manifest structure and need to be # built correctly elif source.name.endswith(".toml"): loaded = tomli.loads(source.read_text()) # Check required structure if ( "tool" not in loaded or "project_composer" not in loaded["tool"] ): raise ComposerManifestError( "TOML manifest must have a section [tool.project_composer] to " "fill base options." ) content = loaded["tool"]["project_composer"] # If there is no composer config name, try to use the TOML project one if not content.get("name"): pyproject_name = loaded.get("project", {}).get("name") if pyproject_name: content["name"] = pyproject_name # No recognized format else: raise ComposerManifestError( "Unable to guess the manifest file format. Please suffix your " "filename either with '.json' or '.toml' depending it is a JSON " "or a TOML format." ) # Build Manitest keyword arguments from retrieved content, only retains items # knowed as manifest fields kwargs = { k: v for k, v in content.items() if k in cls.get_fieldnames() } return cls(**kwargs)