diff --git a/docs/source/conf.py b/docs/source/conf.py index ff4a4623d..6795337f3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,6 +6,10 @@ import os import sys +import sphinx.domains +import sphinx.addnodes +import sphinx.application + # Add path to rez's source. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'src'))) @@ -39,6 +43,17 @@ templates_path = ['_templates'] +nitpick_ignore = [ + # TODO: Remove once we unvendor enum. + ("py:class", "rez.solver._Common"), + ("py:class", "_thread._local"), + ("py:class", "rez.utils.platform_._UnixPlatform") +] + +nitpick_ignore_regex = [ + ("py:class", r"rez\.vendor\..*"), +] + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output @@ -70,6 +85,12 @@ # autoclass_content = 'both' autodoc_class_signature = 'separated' autodoc_member_order = 'bysource' +autodoc_inherit_docstrings = True +autodoc_default_options = { + "show-inheritance": True, + "undoc-members": True, + "inherited-members": True, +} # -- Options for extlinks extension ----------------------------------------- @@ -82,4 +103,40 @@ # -- Options for todo extension --------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/extensions/todo.html -todo_emit_warnings = True +todo_emit_warnings = False + + +# -- Custom ----------------------------------------------------------------- + +def handle_ref_warning( + app: sphinx.application.Sphinx, + domain: sphinx.domains.Domain, + node: sphinx.addnodes.pending_xref, +) -> bool | None: + """ + Emitted when a cross-reference to an object cannot be resolved even + after missing-reference. If the event handler can emit warnings for the + missing reference, it should return True. The configuration variables + nitpick_ignore and nitpick_ignore_regex prevent the event from being + emitted for the corresponding nodes. + """ + if domain and domain.name != 'py': + return None + + from docutils.utils import get_source_line + + source, line = get_source_line(node) + if 'docstring of collections.abc.' in source: + # Silence warnings that come from collections.abc + return True + + return False + + +def setup(app: sphinx.application.Sphinx) -> dict[str, bool | str]: + app.connect('warn-missing-reference', handle_ref_warning) + + return { + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/src/rez/build_system.py b/src/rez/build_system.py index be70ea7a9..bc168deda 100644 --- a/src/rez/build_system.py +++ b/src/rez/build_system.py @@ -28,7 +28,7 @@ def get_valid_build_systems(working_dir, package=None): must be present for the 'custom' build system type. Returns: - List of class: Valid build system class types. + list[type[BuildSystem]]: Valid build system class types. """ from rez.plugin_managers import plugin_manager from rez.exceptions import PackageMetadataError @@ -221,16 +221,17 @@ def build(self, context, variant, build_path, install_path, install=False, build_type: A BuildType (i.e local or central). Returns: - A dict containing the following information: + dict: A dict containing the following information: + - success: Bool indicating if the build was successful. - extra_files: List of created files of interest, not including - build targets. A good example is the interpreted context file, - usually named 'build.rxt.sh' or similar. These files should be - located under build_path. Rez may install them for debugging - purposes. + build targets. A good example is the interpreted context file, + usually named 'build.rxt.sh' or similar. These files should be + located under build_path. Rez may install them for debugging + purposes. - build_env_script: If this instance was created with write_build_scripts - as True, then the build should generate a script which, when run - by the user, places them in the build environment. + as True, then the build should generate a script which, when run + by the user, places them in the build environment. """ raise NotImplementedError diff --git a/src/rez/package_cache.py b/src/rez/package_cache.py index 88c2fba67..5a4733309 100644 --- a/src/rez/package_cache.py +++ b/src/rez/package_cache.py @@ -149,7 +149,7 @@ def add_variant(self, variant, force=False): is no guarantee the resulting variant payload will be functional). Returns: - 2-tuple: + tuple: 2-tuple: - str: Path to cached payload - int: One of VARIANT_FOUND, VARIANT_CREATED, VARIANT_COPYING, VARIANT_COPY_STALLED """ @@ -477,7 +477,8 @@ def get_variants(self): """Get variants and their current statuses from the cache. Returns: - List of 3-tuple: + tuple: List of 3-tuple: + - `Variant`: The cached variant - str: Local cache path for variant, if determined ('' otherwise) - int: Status. One of: diff --git a/src/rez/package_copy.py b/src/rez/package_copy.py index 319c20211..80c6d0f5a 100644 --- a/src/rez/package_copy.py +++ b/src/rez/package_copy.py @@ -31,19 +31,21 @@ def copy_package(package, dest_repository, variants=None, shallow=False, """Copy a package from one package repository to another. This copies the package definition and payload. The package can also be - re-named and/or re-versioned using the `dest_name` and `dest_version` args. + re-named and/or re-versioned using the ``dest_name`` and ``dest_version`` args. The result is a dict describing which package variants were and were not copied. For example: - { - "copied": [ - (`Variant`, `Variant`) - ], - "skipped": [ - (`Variant`, `Variant`) - ] - } + .. code-block:: text + + { + "copied": [ + (`Variant`, `Variant`) + ], + "skipped": [ + (`Variant`, `Variant`) + ] + } Each 2-tuple in the 'copied' or 'skipped' list contains the source and destination variant respectively. In the 'skipped' list, the source variant @@ -51,21 +53,21 @@ def copy_package(package, dest_repository, variants=None, shallow=False, target variant that caused the source not to be copied. Skipped variants will only be present when `overwrite` is False. - Note: - Whether or not a package can be copied is determined by its 'relocatable' - attribute (see the `default_relocatable` config setting for more details). - An attempt to copy a non-relocatable package will fail. You can override - this behaviour with the `force` argument. + .. note:: + Whether or not a package can be copied is determined by its :attr:`relocatable` + attribute (see the :data:`default_relocatable` config setting for more details). + An attempt to copy a non-relocatable package will fail. You can override + this behaviour with the ``force`` argument. Args: - package (`Package`): Package to copy. - dest_repository (`PackageRepository` or str): The package repository, or + package (Package): Package to copy. + dest_repository (PackageRepository or str): The package repository, or a package repository path, to copy the package into. - variants (list of int): Indexes of variants to build, or all if None. + variants (list[int]): Indexes of variants to build, or all if None. shallow (bool): If True, symlinks of each variant's root directory are created, rather than the payload being copied. dest_name (str): If provided, copy the package to a new package name. - dest_version (str or `Version`): If provided, copy the package to a new + dest_version (str or Version): If provided, copy the package to a new version. overwrite (bool): Overwrite variants if they already exist in the destination package. In this case, the existing payload is removed @@ -81,7 +83,7 @@ def copy_package(package, dest_repository, variants=None, shallow=False, is kept intact. Note that this will have no effect if variant(s) are copied into an existing package. skip_payload (bool): If True, do not copy the package payload. - overrides (dict): See `PackageRepository.install_variant`. + overrides (dict): See :meth:`.PackageRepository.install_variant`. verbose (bool): Verbose mode. dry_run (bool): Dry run mode. Dest variants in the result will be None in this case. diff --git a/src/rez/package_filter.py b/src/rez/package_filter.py index b234c8d7d..d76139b2e 100644 --- a/src/rez/package_filter.py +++ b/src/rez/package_filter.py @@ -17,14 +17,16 @@ class PackageFilterBase(object): + """Base class for package filters.""" + def excludes(self, package): """Determine if the filter excludes the given package. Args: - package (`Package`): Package to filter. + package (Package): Package to filter. Returns: - `Rule` object that excludes the package, or None if the package was + typing.Optional[Rule]: Rule object that excludes the package, or None if the package was not excluded. """ raise NotImplementedError @@ -33,7 +35,7 @@ def add_exclusion(self, rule): """Add an exclusion rule. Args: - rule (`Rule`): Rule to exclude on. + rule (Rule): Rule to exclude on. """ raise NotImplementedError @@ -41,7 +43,7 @@ def add_inclusion(self, rule): """Add an inclusion rule. Args: - rule (`Rule`): Rule to include on. + rule (Rule): Rule to include on. """ raise NotImplementedError @@ -51,21 +53,25 @@ def from_pod(cls, data): raise NotImplementedError def to_pod(self): - """Convert to POD type, suitable for storing in an rxt file.""" + """Convert to POD type, suitable for storing in an rxt file. + + Returns: + dict[str, list[str]]: + """ raise NotImplementedError def iter_packages(self, name, range_=None, paths=None): - """Same as iter_packages in packages.py, but also applies this filter. + """Same as :func:`~rez.packages.iter_packages`, but also applies this filter. Args: name (str): Name of the package, eg 'maya'. range_ (VersionRange or str): If provided, limits the versions returned - to those in `range_`. - paths (list of str, optional): paths to search for packages, defaults - to `config.packages_path`. + to those in ``range_``. + paths (typing.Optional[list[str]]): paths to search for packages, defaults + to :data:`packages_path`. Returns: - `Package` iterator. + typing.Iterator[Package]: iterator """ for package in iter_packages(name, range_, paths): if not self.excludes(package): @@ -73,6 +79,12 @@ def iter_packages(self, name, range_=None, paths=None): @property def sha1(self): + """ + SHA1 representation + + Returns: + str: + """ return sha1(str(self).encode("utf-8")).hexdigest() def __repr__(self): @@ -80,16 +92,15 @@ def __repr__(self): class PackageFilter(PackageFilterBase): - """A package filter. - + """ A package filter is a set of rules that hides some packages but leaves others visible. For example, a package filter might be used to hide all packages - whos version ends in the string '.beta'. A package filter might also be used + whos version ends in the string ``.beta``. A package filter might also be used simply to act as a blacklist, hiding some specific packages that are known to be problematic. Rules can be added as 'exclusion' or 'inclusion' rules. A package is only - excluded iff it matches one or more exclusion rules, and does not match any + excluded if it matches one or more exclusion rules, and does not match any inclusion rules. """ def __init__(self): @@ -175,6 +186,11 @@ def cost(self): @classmethod def from_pod(cls, data): + """Convert from POD types to equivalent package filter. + + Returns: + PackageFilter: + """ f = PackageFilter() for namespace, func in (("excludes", f.add_exclusion), ("includes", f.add_inclusion)): @@ -243,6 +259,8 @@ def add_exclusion(self, rule): def add_inclusion(self, rule): """ + See also: :meth:`PackageFilterBase.add_inclusion` + Note: Adding an inclusion to a filter list applies that inclusion across all filters. @@ -251,6 +269,11 @@ def add_inclusion(self, rule): f.add_inclusion(rule) def excludes(self, package): + """Returns the first rule that exlcudes ``package``, if any. + + Returns: + Rule: + """ for f in self.filters: rule = f.excludes(package) if rule: @@ -268,6 +291,11 @@ def copy(self): @classmethod def from_pod(cls, data): + """Convert from POD types to equivalent package filter. + + Returns: + PackageFilterList: + """ flist = PackageFilterList() for dict_ in data: f = PackageFilter.from_pod(dict_) @@ -291,23 +319,29 @@ def __str__(self): @cached_class_property def singleton(cls): - """Filter list as configured by rezconfig.package_filter.""" + """Filter list as configured by :data:`package_filter`. + + Returns: + PackageFilterList: + """ return cls.from_pod(config.package_filter) -# filter that does not exclude any packages +#: filter that does not exclude any packages no_filter = PackageFilterList() class Rule(object): + """Base package filter rule""" + + #: Rule name name = None - """Relative cost of rule - cheaper rules are checked first.""" def match(self, package): """Apply the rule to the package. Args: - package (`Package`): Package to filter. + package (Package): Package to filter. Returns: bool: True if the package matches the filter, False otherwise. @@ -316,7 +350,11 @@ def match(self, package): def family(self): """Returns a package family string if this rule only applies to a given - package family, otherwise None.""" + package family, otherwise None. + + Returns: + str | None: + """ return self._family def cost(self): @@ -327,13 +365,13 @@ def cost(self): def parse_rule(cls, txt): """Parse a rule from a string. - See rezconfig.package_filter for an overview of valid strings. + See :data:`package_filter` for an overview of valid strings. Args: txt (str): String to parse. Returns: - `Rule` instance. + Rule: """ types = {"glob": GlobRule, "regex": RegexRule, @@ -414,7 +452,7 @@ def __str__(self): class RegexRule(RegexRuleBase): """A rule that matches a package if its qualified name matches a regex string. - For example, the package 'foo-1.beta' would match the regex rule '.*\\.beta$'. + For example, the package ``foo-1.beta`` would match the regex rule ``.*\\.beta$``. """ name = "regex" @@ -422,7 +460,7 @@ def __init__(self, s): """Create a regex rule. Args: - s (str): Regex pattern. Eg '.*\\.beta$'. + s (str): Regex pattern. Eg ``.*\\.beta$``. """ self.txt = s self._family = self._extract_family(s) @@ -432,7 +470,7 @@ def __init__(self, s): class GlobRule(RegexRuleBase): """A rule that matches a package if its qualified name matches a glob string. - For example, the package 'foo-1.2' would match the glob rule 'foo-*'. + For example, the package ``foo-1.2`` would match the glob rule ``foo-*``. """ name = "glob" @@ -440,7 +478,7 @@ def __init__(self, s): """Create a glob rule. Args: - s (str): Glob pattern. Eg 'foo.*', '*.beta'. + s (str): Glob pattern. Eg ``foo.*``, ``*.beta``. """ self.txt = s self._family = self._extract_family(s) @@ -451,7 +489,7 @@ class RangeRule(Rule): """A rule that matches a package if that package does not conflict with a given requirement. - For example, the package 'foo-1.2' would match the requirement rule 'foo<10'. + For example, the package ``foo-1.2`` would match the requirement rule ``foo<10``. """ name = "range" @@ -480,13 +518,13 @@ class TimestampRule(Rule): given timestamp. Note: - The 'timestamp' argument used for resolves is ANDed with any package - filters - providing a filter containing timestamp rules does not override - the value of 'timestamp'. + The ``timestamp`` argument used for resolves is ANDed with any package + filters. Providing a filter containing timestamp rules does not override + the value of ``timestamp``. - Note: - Do NOT use a timestamp rule to mimic what the 'timestamp' resolve argument - does. 'timestamp' is treated differently - the memcache caching system + Warning: + Do NOT use a timestamp rule to mimic what the ``timestamp`` resolve argument + does. ``timestamp`` is treated differently - the memcache caching system is aware of it, so timestamped resolves get cached. Non-timestamped resolves also get cached, but their cache entries are invalidated more often (when new packages are released). diff --git a/src/rez/package_maker.py b/src/rez/package_maker.py index 8e786a76e..83dac81a1 100644 --- a/src/rez/package_maker.py +++ b/src/rez/package_maker.py @@ -175,9 +175,9 @@ def make_package(name, path, make_base=None, make_root=None, skip_existing=True, Args: name (str): Package name. path (str): Package repository path to install package into. - make_base (callable): Function that is used to create the package + make_base (typing.Callable): Function that is used to create the package payload, if applicable. - make_root (callable): Function that is used to create the package + make_root (typing.Callable): Function that is used to create the package variant payloads, if applicable. skip_existing (bool): If True, detect if a variant already exists, and skip with a warning message if so. diff --git a/src/rez/package_order.py b/src/rez/package_order.py index 15aa61e69..9d3aaac65 100644 --- a/src/rez/package_order.py +++ b/src/rez/package_order.py @@ -12,6 +12,8 @@ class PackageOrder(object): """Package reorderer base class.""" + + #: Orderer name name = None def __init__(self): @@ -31,11 +33,12 @@ def reorder(self, iterable, key=None): Args: iterable: Iterable list of packages, or objects that contain packages. - key (callable): Callable, where key(iterable) gives a `Package`. If - None, iterable is assumed to be a list of `Package` objects. + key (typing.Callable[typing.Any, Package]): Callable, where key(iterable) + gives a :class:`~rez.packages.Package`. If None, iterable is assumed + to be a list of :class:`~rez.packages.Package` objects. Returns: - List of `iterable` type, reordered. + list: Reordered ``iterable`` """ raise NotImplementedError @@ -64,7 +67,7 @@ class NullPackageOrder(PackageOrder): This orderer is useful in cases where you want to apply some default orderer to a set of packages, but may want to explicitly NOT reorder a particular - package. You would use a `NullPackageOrder` in a `PerFamilyOrder` to do this. + package. You would use a :class:`NullPackageOrder` in a :class:`PerFamilyOrder` to do this. """ name = "no_order" @@ -81,7 +84,9 @@ def to_pod(self): """ Example (in yaml): - type: no_order + .. code-block:: yaml + + type: no_order """ return {} @@ -91,7 +96,7 @@ def from_pod(cls, data): class SortedOrder(PackageOrder): - """An orderer that sorts wrt version. + """An orderer that sorts based on :attr:`Package.version `. """ name = "sorted" @@ -116,8 +121,10 @@ def to_pod(self): """ Example (in yaml): - type: sorted - descending: true + .. code-block:: yaml + + type: sorted + descending: true """ return {"descending": self.descending} @@ -135,10 +142,10 @@ def __init__(self, order_dict, default_order=None): """Create a reorderer. Args: - order_dict (dict of (str, `PackageOrder`): Orderers to apply to + order_dict (dict[str, PackageOrder]): Orderers to apply to each package family. - default_order (`PackageOrder`): Orderer to apply to any packages - not specified in `order_dict`. + default_order (PackageOrder): Orderer to apply to any packages + not specified in ``order_dict``. """ self.order_dict = order_dict.copy() self.default_order = default_order @@ -175,17 +182,19 @@ def to_pod(self): """ Example (in yaml): - type: per_family - orderers: - - packages: ['foo', 'bah'] - type: version_split - first_version: '4.0.5' - - packages: ['python'] - type: sorted - descending: false - default_order: - type: sorted - descending: true + .. code-block:: yaml + + type: per_family + orderers: + - packages: ['foo', 'bah'] + type: version_split + first_version: '4.0.5' + - packages: ['python'] + type: sorted + descending: false + default_order: + type: sorted + descending: true """ orderers = {} packages = {} @@ -234,7 +243,7 @@ class VersionSplitPackageOrder(PackageOrder): """Orders package versions <= a given version first. For example, given the versions [5, 4, 3, 2, 1], an orderer initialized - with version=3 would give the order [3, 2, 1, 5, 4]. + with ``version=3`` would give the order [3, 2, 1, 5, 4]. """ name = "version_split" @@ -242,7 +251,7 @@ def __init__(self, first_version): """Create a reorderer. Args: - first_version (`Version`): Start with versions <= this value. + first_version (Version): Start with versions <= this value. """ self.first_version = first_version @@ -281,8 +290,10 @@ def to_pod(self): """ Example (in yaml): - type: version_split - first_version: "3.0.0" + .. code-block:: yaml + + type: version_split + first_version: "3.0.0" """ return dict(first_version=str(self.first_version)) @@ -294,37 +305,41 @@ def from_pod(cls, data): class TimestampPackageOrder(PackageOrder): """A timestamp order function. - Given a time T, this orderer returns packages released before T, in descending - order, followed by those released after. If `rank` is non-zero, version + Given a time ``T``, this orderer returns packages released before ``T``, in descending + order, followed by those released after. If ``rank`` is non-zero, version changes at that rank and above are allowed over the timestamp. For example, consider the common case where we want to prioritize packages - released before T, except for newer patches. Consider the following package - versions, and time T: - - 2.2.1 - 2.2.0 - 2.1.1 - 2.1.0 - 2.0.6 - 2.0.5 - <-- T - 2.0.0 - 1.9.0 - - A timestamp orderer set to rank=3 (patch versions) will attempt to consume + released before ``T``, except for newer patches. Consider the following package + versions, and time ``T``: + + .. code-block:: text + + 2.2.1 + 2.2.0 + 2.1.1 + 2.1.0 + 2.0.6 + 2.0.5 + <-- T + 2.0.0 + 1.9.0 + + A timestamp orderer set to ``rank=3`` (patch versions) will attempt to consume the packages in the following order: - 2.0.6 - 2.0.5 - 2.0.0 - 1.9.0 - 2.1.1 - 2.1.0 - 2.2.1 - 2.2.0 + .. code-block:: text + + 2.0.6 + 2.0.5 + 2.0.0 + 1.9.0 + 2.1.1 + 2.1.0 + 2.2.1 + 2.2.0 - Notice that packages before T are preferred, followed by newer versions. + Notice that packages before ``T`` are preferred, followed by newer versions. Newer versions are consumed in ascending order, except within rank (this is why 2.1.1 is consumed before 2.1.0). """ @@ -421,9 +436,11 @@ def to_pod(self): """ Example (in yaml): - type: soft_timestamp - timestamp: 1234567 - rank: 3 + .. code-block:: yaml + + type: soft_timestamp + timestamp: 1234567 + rank: 3 """ return dict(timestamp=self.timestamp, rank=self.rank) @@ -478,6 +495,14 @@ def from_pod(data): def register_orderer(cls): + """Register an orderer + + Args: + cls (type[PackageOrder]): Package orderer class to register. + + returns: + bool: True if successfully registered, else False. + """ if isclass(cls) and issubclass(cls, PackageOrder) and \ hasattr(cls, "name") and cls.name: _orderers[cls.name] = cls diff --git a/src/rez/package_py_utils.py b/src/rez/package_py_utils.py index d07b45f2f..be493e0fb 100644 --- a/src/rez/package_py_utils.py +++ b/src/rez/package_py_utils.py @@ -20,20 +20,20 @@ def expand_requirement(request, paths=None): - """Expands a requirement string like 'python-2.*', 'foo-2.*+<*', etc. + """Expands a requirement string like ``python-2.*``, ``foo-2.*+<*``, etc. Wildcards are expanded to the latest version that matches. There is also a - special wildcard '**' that will expand to the full version, but it cannot - be used in combination with '*'. + special wildcard ``**`` that will expand to the full version, but it cannot + be used in combination with ``*``. - Wildcards MUST placehold a whole version token, not partial - while 'foo-2.*' - is valid, 'foo-2.v*' is not. + Wildcards MUST placehold a whole version token, not partial - while ``foo-2.*`` + is valid, ``foo-2.v*`` is not. - Wildcards MUST appear at the end of version numbers - while 'foo-1.*.*' is - valid, 'foo-1.*.0' is not. + Wildcards MUST appear at the end of version numbers - while ``foo-1.*.*`` is + valid, ``foo-1.*.0`` is not. It is possible that an expansion will result in an invalid request string - (such as 'foo-2+<2'). The appropriate exception will be raised if this + (such as ``foo-2+<2``). The appropriate exception will be raised if this happens. Examples: @@ -46,9 +46,9 @@ def expand_requirement(request, paths=None): python<3.0.5 Args: - request (str): Request to expand, eg 'python-2.*' - paths (list of str, optional): paths to search for package families, - defaults to `config.packages_path`. + request (str): Request to expand, eg ``python-2.*`` + paths (typing.Optional[list[str]]): paths to search for package families, + defaults to :data:`packages_path`. Returns: str: Expanded request string. @@ -159,17 +159,24 @@ def expand_requires(*requests): ["boost-1.55"] Args: - requests (list of str): Requirements to expand. Each value may have + requests (list[str]): Requirements to expand. Each value may have trailing wildcards. Returns: - List of str: Expanded requirements. + list[str]: Expanded requirements. """ return [expand_requirement(x) for x in requests] def exec_command(attr, cmd): - """Runs a subproc to calculate a package attribute. + """Runs a subprocess to calculate a package attribute. + + Args: + attr (str): Package attribute + cmd (list[str]): Command to run + + Returns: + tuple(str): Returns a tuple of (stdout, stderr). """ import subprocess @@ -189,7 +196,7 @@ def exec_python(attr, src, executable="python"): Args: attr (str): Name of package attribute being created. - src (list of str): Python code to execute, will be converted into + src (list[str]): Python code to execute, will be converted into semicolon-delimited single line of code. Returns: @@ -227,11 +234,11 @@ def find_site_python(module_name, paths=None): Args: module_name (str): Target python module. - paths (list of str, optional): paths to search for packages, - defaults to `config.packages_path`. + paths (typing.Optional[list[str]]): paths to search for packages, + defaults to :data:`packages_path`. Returns: - `Package`: Native python package containing the named module. + Package: Native python package containing the named module. """ from rez.packages import iter_packages import subprocess diff --git a/src/rez/package_remove.py b/src/rez/package_remove.py index cbd35676a..8ed6e3950 100644 --- a/src/rez/package_remove.py +++ b/src/rez/package_remove.py @@ -57,7 +57,7 @@ def remove_packages_ignored_since(days, paths=None, dry_run=False, verbose=False Args: days (int): Remove packages ignored >= this many days - paths (list of str, optional): Paths to search for packages, defaults + paths (typing.Optional[list[str]]): Paths to search for packages, defaults to `config.packages_path`. dry_run: Dry run mode verbose (bool): Verbose mode diff --git a/src/rez/package_repository.py b/src/rez/package_repository.py index 67bba6049..2c7be5ab2 100644 --- a/src/rez/package_repository.py +++ b/src/rez/package_repository.py @@ -108,7 +108,7 @@ def uid(self): database address + index, and so on. Returns: - hashable value: Value that uniquely identifies this repository. + tuple[str, str]: Value that uniquely identifies this repository. """ return self._uid() diff --git a/src/rez/package_resources.py b/src/rez/package_resources.py index 364932457..f4b497249 100644 --- a/src/rez/package_resources.py +++ b/src/rez/package_resources.py @@ -270,13 +270,9 @@ def late_bound(schema): class PackageRepositoryResource(Resource): """Base class for all package-related resources. - - Attributes: - schema_error (`Exception`): Type of exception to throw on bad data. - repository_type (str): Type of package repository associated with this - resource type. """ schema_error = PackageMetadataError + #: Type of package repository associated with this resource type. repository_type = None @classmethod diff --git a/src/rez/package_search.py b/src/rez/package_search.py index f7a25d1a4..6b5f09c4f 100644 --- a/src/rez/package_search.py +++ b/src/rez/package_search.py @@ -46,7 +46,8 @@ def get_reverse_dependency_tree(package_name, depth=None, paths=None, private_build_requires. Returns: - A 2-tuple: + tuple: A 2-tuple: + - (list of list of str): Lists of package names, where each list is a single depth in the tree. The first list is always [`package_name`]. - `pygraph.digraph` object, where nodes are package names, and @@ -219,7 +220,8 @@ def iter_resources(self, resources_request=None): are supported. If None, returns all matching resource types. Returns: - 2-tuple: + tuple: 2-tuple: + - str: resource type (family, package, variant); - Iterator of `ResourceSearchResult`: Matching resources. Will be in alphabetical order if families, and version ascending for @@ -234,7 +236,8 @@ def search(self, resources_request=None): are supported. If None, returns all matching resource types. Returns: - 2-tuple: + tuple: 2-tuple: + - str: resource type (family, package, variant); - List of `ResourceSearchResult`: Matching resources. Will be in alphabetical order if families, and version ascending for @@ -382,7 +385,7 @@ def format_search_results(self, search_results): search_results (list of `ResourceSearchResult`): Search to format. Returns: - List of 2-tuple: Text and color to print in. + tuple: List of 2-tuple: Text and color to print in. """ formatted_lines = [] diff --git a/src/rez/package_serialise.py b/src/rez/package_serialise.py index a962caace..04f6d6de6 100644 --- a/src/rez/package_serialise.py +++ b/src/rez/package_serialise.py @@ -121,7 +121,7 @@ def dump_package_data(data, buf, format_=FileFormat.py, skip_attributes=None): Args: data (dict): Data source - must conform to `package_serialise_schema`. - buf (file-like object): Destination stream. + buf (typing.IO): Destination stream. format_ (`FileFormat`): Format to dump data in. skip_attributes (list of str): List of attributes to not print. """ diff --git a/src/rez/package_test.py b/src/rez/package_test.py index 8215235fb..ddd475e8b 100644 --- a/src/rez/package_test.py +++ b/src/rez/package_test.py @@ -27,14 +27,16 @@ class PackageTestRunner(object): An example tests entry in a package.py might look like this: - tests = { - "unit": "python -m unittest -s {root}/tests", - "CI": { - "command": "python {root}/ci_tests/main.py", - "requires": ["maya-2017"], - "replace": True - } - } + .. code-block:: python + + tests = { + "unit": "python -m unittest -s {root}/tests", + "CI": { + "command": "python {root}/ci_tests/main.py", + "requires": ["maya-2017"], + "replace": True + } + } By default tests are run in an environment containing the current package. @@ -42,8 +44,8 @@ class PackageTestRunner(object): command. If a dict, the "command" string is the command, and the "requires" list is added to the test env. - Command strings automatically expand references such as '{root}', much - as happens in a *commands* function. + Command strings automatically expand references such as ``{root}``, much + as happens in a :data:`commands` function. Commands can also be a list - in this case, the test process is launched directly, rather than interpreted via a shell. @@ -55,23 +57,23 @@ def __init__(self, package_request, use_current_env=False, """Create a package tester. Args: - package_request (str or `PackageRequest`): The package to test. + package_request (str or PackageRequest): The package to test. use_current_env (bool): If True, run the test directly in the current rez-resolved environment, if there is one, and if it contains packages that meet the test's requirements. - extra_package_requests (list of str or `PackageRequest`): Extra + extra_package_requests (list[str] or PackageRequest): Extra requests, these are appended to the test environment. package_paths: List of paths to search for pkgs, defaults to - config.packages_path. - stdout (file-like object): Defaults to sys.stdout. - stderr (file-like object): Defaults to sys.stderr. + :data:`packages_path`. + stdout (typing.IO): Defaults to :data:`sys.stdout`. + stderr (typing.IO): Defaults to :data:`sys.stderr`. verbose (int): Verbose mode (valid values: 0, 1, 2) dry_run (bool): If True, do everything except actually run tests. - cumulative_test_results (`PackageTestResults`): If supplied, test + cumulative_test_results (PackageTestResults): If supplied, test run results can be stored across multiple runners. - context_kwargs: Extra arguments which are passed to the - `ResolvedContext` instances used to run the tests within. - Ignored if `use_current_env` is True. + context_kwargs (dict[typing.Any, typing.Any]): Extra arguments which are passed to the + :class:`~rez.resolved_context.ResolvedContext` instances used to run the tests within. + Ignored if ``use_current_env`` is True. """ self.package_request = package_request self.use_current_env = use_current_env @@ -105,7 +107,7 @@ def get_package(self): """Get the target package. Returns: - `Package`: Package to run tests on. + Package: Package to run tests on. """ if self.package is not None: return self.package @@ -616,7 +618,7 @@ def _get_context(self, requires, quiet=False): def _get_target_variants(self, test_name): """ If the test is not variant-specific, then attempt to find the 'preferred' - variant (as per setting 'variant_select_mode'). Otherwise, just run tests + variant (as per setting :data:`variant_select_mode`). Otherwise, just run tests over all variants. """ package = self.get_package() @@ -655,9 +657,9 @@ def _get_target_variants(self, test_name): class PackageTestResults(object): - """Contains results of running tests with a `PackageTestRunner`. + """Contains results of running tests with a :class:`PackageTestRunner`. - Use this class (and pass it to the `PackageTestRunner` constructor) if you + Use this class (and pass it to the :class:`PackageTestRunner` constructor) if you need to gather test run results from separate runners, and display them in a single table. """ diff --git a/src/rez/packages.py b/src/rez/packages.py index 2cac8f24a..fab8b292e 100644 --- a/src/rez/packages.py +++ b/src/rez/packages.py @@ -117,7 +117,7 @@ def print_info(self, buf=None, format_=FileFormat.yaml, """Print the contents of the package. Args: - buf (file-like object): Stream to write to. + buf (typing.IO): Stream to write to. format_ (`FileFormat`): Format to write in. skip_attributes (list of str): List of attributes to not print. include_release (bool): If True, include release-related attributes, @@ -530,7 +530,7 @@ def iter_package_families(paths=None): families. Args: - paths (list of str, optional): paths to search for package families, + paths (typing.Optional[list[str]]): paths to search for package families, defaults to `config.packages_path`. Returns: @@ -553,7 +553,7 @@ def iter_packages(name, range_=None, paths=None): name (str): Name of the package, eg 'maya'. range_ (VersionRange or str): If provided, limits the versions returned to those in `range_`. - paths (list of str, optional): paths to search for packages, defaults + paths (typing.Optional[list[str]]): paths to search for packages, defaults to `config.packages_path`. Returns: @@ -584,7 +584,7 @@ def get_package(name, version, paths=None): Args: name (str): Name of the package, eg 'maya'. version (Version or str): Version of the package, eg '1.0.0' - paths (list of str, optional): paths to search for package, defaults + paths (typing.Optional[list[str]]): paths to search for package, defaults to `config.packages_path`. Returns: @@ -665,7 +665,7 @@ def get_package_from_string(txt, paths=None): Args: txt (str): String such as 'foo', 'bah-1.3'. - paths (list of str, optional): paths to search for package, defaults + paths (typing.Optional[list[str]]): paths to search for package, defaults to `config.packages_path`. Returns: @@ -914,7 +914,7 @@ def get_latest_package(name, range_=None, paths=None, error=False): Args: name (str): Package name. range_ (`VersionRange`): Version range to search within. - paths (list of str, optional): paths to search for package families, + paths (typing.Optional[list[str]]): paths to search for package families, defaults to `config.packages_path`. error (bool): If True, raise an error if no package is found. @@ -937,7 +937,7 @@ def get_latest_package_from_string(txt, paths=None, error=False): Args: txt (str): Request, eg 'foo-1.2+' - paths (list of str, optional): paths to search for packages, defaults + paths (typing.Optional[list[str]]): paths to search for packages, defaults to `config.packages_path`. error (bool): If True, raise an error if no package is found. diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index a5a16bc36..2f46fd2b2 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -383,7 +383,7 @@ def get_failed_plugins(self, plugin_type): """Return a list of plugins for the given type that failed to load. Returns: - List of 2-tuples: + tuple: List of 2-tuples: name (str): Name of the plugin. reason (str): Error message. """ diff --git a/src/rez/release_vcs.py b/src/rez/release_vcs.py index a8a720ae5..31c749636 100644 --- a/src/rez/release_vcs.py +++ b/src/rez/release_vcs.py @@ -162,7 +162,7 @@ def get_changelog(self, previous_revision=None, max_revisions=None): Args: previous_revision: The revision to give the changelog since. If - None, give the entire changelog. + None, give the entire changelog. Returns: Changelog, as a string. diff --git a/src/rez/resolved_context.py b/src/rez/resolved_context.py index 79a76299f..fea035a67 100644 --- a/src/rez/resolved_context.py +++ b/src/rez/resolved_context.py @@ -60,23 +60,32 @@ class RezToolsVisibility(Enum): """Determines if/how rez cli tools are added back to PATH within a - resolved environment.""" - never = 0 # Don't expose rez in resolved env - append = 1 # Append to PATH in resolved env - prepend = 2 # Prepend to PATH in resolved env + resolved environment. + """ + #: Don't expose rez in resolved env + never = 0 + #: Append to PATH in resolved env + append = 1 + #: Prepend to PATH in resolved env + prepend = 2 class SuiteVisibility(Enum): """Defines what suites on $PATH stay visible when a new rez environment is - resolved.""" - never = 0 # Don't attempt to keep any suites visible in a new env - always = 1 # Keep suites visible in any new env - parent = 2 # Keep only the parent suite of a tool visible - parent_priority = 3 # Keep all suites visible and the parent takes precedence + resolved. + """ + #: Don't attempt to keep any suites visible in a new env + never = 0 + #: Keep suites visible in any new env + always = 1 + #: Keep only the parent suite of a tool visible + parent = 2 + #: Keep all suites visible and the parent takes precedence + parent_priority = 3 class PatchLock(Enum): - """ Enum to represent the 'lock type' used when patching context objects. + """Enum to represent the 'lock type' used when patching context objects. """ no_lock = ("No locking", -1) lock_2 = ("Minor version updates only (X.*)", 1) @@ -107,7 +116,7 @@ def get_lock_request(name, version, patch_lock, weak=True): patch_lock (PatchLock): Lock type to apply. Returns: - `PackageRequest` object, or None if there is no equivalent request. + typing.Optional[PackageRequest]: PackageRequest object, or None if there is no equivalent request. """ ch = '~' if weak else '' if patch_lock == PatchLock.lock: @@ -168,41 +177,40 @@ def __init__(self, package_requests, verbosity=0, timestamp=None, """Perform a package resolve, and store the result. Args: - package_requests: List of strings or PackageRequest objects - representing the request. - verbosity: Verbosity level. One of [0,1,2]. - timestamp: Ignore packages released after this epoch time. Packages + package_requests (list[typing.Union[str, PackageRequest]]): request + verbosity (int): Verbosity level. One of [0,1,2]. + timestamp (float): Ignore packages released after this epoch time. Packages released at exactly this time will not be ignored. - building: True if we're resolving for a build. - caching: If True, cache(s) may be used to speed the resolve. If - False, caches will not be used. If None, config.resolve_caching + building (bool): True if we're resolving for a build. + caching (bool): If True, cache(s) may be used to speed the resolve. If + False, caches will not be used. If None, :data:`resolve_caching` is used. - package_paths: List of paths to search for pkgs, defaults to - config.packages_path. - package_filter (`PackageFilterBase`): Filter used to exclude certain - packages. Defaults to settings from config.package_filter. Use - `package_filter.no_filter` to remove all filtering. - package_orderers (list of `PackageOrder`): Custom package ordering. - Defaults to settings from config.package_orderers. - add_implicit_packages: If True, the implicit package list defined - by config.implicit_packages is appended to the request. + package_paths (list[str]): List of paths to search for pkgs, defaults to + :data:`packages_path`. + package_filter (PackageFilterBase): Filter used to exclude certain + packages. Defaults to settings from :data:`package_filter`. Use + :data:`rez.package_filter.no_filter` to remove all filtering. + package_orderers (list[PackageOrder]): Custom package ordering. + Defaults to settings from :data:`package_orderers`. + add_implicit_packages (bool): If True, the implicit package list defined + by :data:`implicit_packages` is appended to the request. max_fails (int): Abort the resolve if the number of failed steps is greater or equal to this number. If -1, does not abort. time_limit (int): Abort the resolve if it takes longer than this many seconds. If -1, there is no time limit. - callback: See `Solver`. + callback: See :class:`.Solver`. package_load_callback: If not None, this callable will be called prior to each package being loaded. It is passed a single - `Package` object. - buf (file-like object): Where to print verbose output to, defaults + :class:`.Package` object. + buf (typing.IO): Where to print verbose output to, defaults to stdout. suppress_passive (bool): If True, don't print debugging info that has had no effect on the solve. This argument only has an - effect if `verbosity` > 2. + effect if ``verbosity`` > 2. print_stats (bool): If True, print advanced solver stats at the end. package_caching (bool|None): If True, apply package caching settings as per the config. If None, enable as determined by config - setting 'package_cache_during_build'. + setting :data:`package_cache_during_build`. """ self.load_path = None @@ -357,7 +365,7 @@ def status(self): """Return the current status of the context. Returns: - ResolverStatus. + ResolverStatus: """ return self.status_ @@ -369,7 +377,7 @@ def requested_packages(self, include_implicit=False): to the result. Returns: - List of `PackageRequest` objects. + list[PackageRequest]: """ if include_implicit: return self._package_requests + self.implicit_packages @@ -381,7 +389,7 @@ def resolved_packages(self): """Get packages in the resolve. Returns: - List of `Variant` objects, or None if the resolve failed. + typing.Optional[list[Variant]]: Resolved variant objects, or None if the resolve failed. """ return self._resolved_packages @@ -390,7 +398,7 @@ def resolved_ephemerals(self): """Get non-conflict ephemerals in the resolve. Returns: - List of `Requirement` objects, or None if the resolve failed. + typing.Optional[list[Requirement]]: Requirement objects, or None if the resolve failed. """ return self._resolved_ephemerals @@ -457,11 +465,11 @@ def retargeted(self, package_paths, package_names=None, skip_missing=False): package_names (list of str): Only retarget these packages. If None, retarget all packages. skip_missing (bool): If True, skip retargeting of variants that - cannot be found in `package_paths`. By default, a - `PackageNotFoundError` is raised. + cannot be found in ``package_paths``. By default, a + :exc:`.PackageNotFoundError` is raised. Returns: - ResolvecContext`: The retargeted context. + ResolvedContext: The retargeted context. """ retargeted_variants = [] @@ -529,9 +537,9 @@ def get_patched_request(self, package_requests=None, in the order that they appear in `package_requests`. Args: - package_requests (list of str or list of `PackageRequest`): + package_requests (list[typing.Union[str, PackageRequest]): Overriding requests. - package_subtractions (list of str): Any original request with a + package_subtractions (list[str]): Any original request with a package name in this list is removed, before the new requests are added. strict (bool): If True, the current context's resolve is used as the @@ -540,12 +548,12 @@ def get_patched_request(self, package_requests=None, and further - for example, rank=3 means that only version patch numbers are allowed to increase, major and minor versions will not change. This is only applied to packages that have not been - explicitly overridden in `package_requests`. If rank <= 1, or - `strict` is True, rank is ignored. + explicitly overridden in ``package_requests``. If rank <= 1, or + ``strict`` is True, rank is ignored. Returns: - List of `PackageRequest` objects that can be used to construct a - new `ResolvedContext` object. + list[PackageRequest]: PackageRequests objects that can be used to construct a + new :class:`ResolvedContext` object. """ # assemble source request if strict: @@ -663,7 +671,7 @@ def get_current(cls): """Get the context for the current env, if there is one. Returns: - `ResolvedContext`: Current context, or None if not in a resolved env. + ResolvedContext: Current context, or None if not in a resolved env. """ filepath = os.getenv("REZ_RXT_FILE") if not filepath or not os.path.exists(filepath): @@ -717,15 +725,16 @@ def get_resolve_diff(self, other): of a package is ignored. Returns: - A dict containing: + dict: A dict containing: + - 'newer_packages': A dict containing items: - - package name (str); - - List of `Package` objects. These are the packages up to and - including the newer package in `self`, in ascending order. + - package name (str); + - List of `Package` objects. These are the packages up to and + including the newer package in `self`, in ascending order. - 'older_packages': A dict containing: - - package name (str); - - List of `Package` objects. These are the packages down to and - including the older package in `self`, in descending order. + - package name (str); + - List of `Package` objects. These are the packages down to and + including the older package in `self`, in descending order. - 'added_packages': Set of `Package` objects present in `self` but not in `other`; - 'removed_packages': Set of `Package` objects present in `other`, @@ -795,7 +804,7 @@ def print_info(self, buf=sys.stdout, verbosity=0, source_order=False, """Prints a message summarising the contents of the resolved context. Args: - buf (file-like object): Where to print this info to. + buf (typing.IO): Where to print this info to. verbosity (bool): Verbose mode. source_order (bool): If True, print resolved packages in the order they are sourced, rather than alphabetical order. @@ -984,8 +993,9 @@ def print_resolve_diff(self, other, heading=None): """Print the difference between the resolve of two contexts. Args: - other (`ResolvedContext`): Context to compare to. + other (ResolvedContext): Context to compare to. heading: One of: + - None: Do not display a heading; - True: Display the filename of each context as a heading, if both contexts have a filepath; @@ -1107,9 +1117,12 @@ def validate(self): def get_environ(self, parent_environ=None): """Get the environ dict resulting from interpreting this context. - @param parent_environ Environment to interpret the context within, - defaults to os.environ if None. - @returns The environment dict generated by this context, when + Args: + parent_environ: Environment to interpret the context within, + defaults to os.environ if None. + + Returns: + The environment dict generated by this context, when interpreted in a python rex interpreter. """ interp = Python(target_environ={}, passive=True) @@ -1127,7 +1140,7 @@ def get_key(self, key, request_only=False): packages that were also present in the request. Returns: - Dict of {pkg-name: (variant, value)}. + Dict of ``{pkg-name: (variant, value)}``. """ values = {} requested_names = [x.name for x in self._package_requests @@ -1150,7 +1163,7 @@ def get_tools(self, request_only=False): that were also present in the request. Returns: - Dict of {pkg-name: (variant, [tools])}. + Dict of ``{pkg-name: (variant, [tools])}``. """ return self.get_key("tools", request_only=request_only) @@ -1184,7 +1197,7 @@ def get_conflicting_tools(self, request_only=False): that were also present in the request. Returns: - Dict of {tool-name: set([Variant])}. + Dict of ``{tool-name: set([Variant])}``. """ from collections import defaultdict @@ -1206,7 +1219,7 @@ def get_shell_code(self, shell=None, parent_environ=None, style=OutputStyle.file type is used. parent_environ (dict): Environment to interpret the context within, defaults to os.environ if None. - style (): Style to format shell code in. + style (OutputStyle): Style to format shell code in. """ executor = self._create_executor(interpreter=create_shell(shell), parent_environ=parent_environ) @@ -1223,7 +1236,7 @@ def get_actions(self, parent_environ=None): context. This is provided mainly for testing purposes. Args: - parent_environ Environment to interpret the context within, + parent_environ: Environment to interpret the context within, defaults to os.environ if None. Returns: @@ -1325,7 +1338,7 @@ def execute_rex_code(self, code, filename=None, shell=None, Popen_args: args to pass to the shell process object constructor. Returns: - `subprocess.Popen` object for the shell process. + subprocess.Popen: Subprocess object for the shell process. """ def _actions_callback(executor): executor.execute_code(code, filename=filename) @@ -1383,10 +1396,11 @@ def execute_shell(self, shell=None, parent_environ=None, rcfile=None, Popen_args: args to pass to the shell process object constructor. Returns: - If blocking: A 3-tuple of (returncode, stdout, stderr). + If blocking, a 3-tuple of (returncode, stdout, stderr). Note that if you want to get anything other than None for stdout and/or stderr, you need to give stdout=PIPE and/or stderr=PIPE. - If non-blocking - A subprocess.Popen object for the shell process. + + If non-blocking, a subprocess.Popen object for the shell process. """ sh = create_shell(shell) diff --git a/src/rez/rex.py b/src/rez/rex.py index ac76c143d..1613f8de4 100644 --- a/src/rez/rex.py +++ b/src/rez/rex.py @@ -906,7 +906,7 @@ def formatted(self, func): """Return the string with non-literal parts formatted. Args: - func (callable): Callable that translates a string into a + func (typing.Callable): Callable that translates a string into a formatted string. Returns: diff --git a/src/rez/rex_bindings.py b/src/rez/rex_bindings.py index 62b45acb6..9deca06f8 100644 --- a/src/rez/rex_bindings.py +++ b/src/rez/rex_bindings.py @@ -38,6 +38,8 @@ def __getattr__(self, attr): class VersionBinding(Binding): """Binds a version.Version object. + Examples: + >>> v = VersionBinding(Version("1.2.3alpha")) >>> v.major 1 @@ -212,11 +214,14 @@ def get_range(self, name, default=None): class EphemeralsBinding(RO_MappingBinding): """Binds a list of resolved ephemeral packages. - Note that the leading '.' is implied when referring to ephemerals. Eg: + Note: + The leading '.' is implied when referring to ephemerals. Eg: + + .. code-block:: python - # in package.py - def commands(): - if "foo.cli" in ephemerals: # will match '.foo.cli-*' request + # in package.py + def commands(): + if "foo.cli" in ephemerals: # will match '.foo.cli-*' request """ def __init__(self, ephemerals): doc = dict( @@ -244,24 +249,28 @@ def intersects(obj, range_): Examples: - # in package.py - def commands(): - # test a request - if intersects(request.maya, '2019+'): - info('requested maya allows >=2019.*') + .. code-block:: python + + # in package.py + def commands(): + # test a request + if intersects(request.maya, '2019+'): + info('requested maya allows >=2019.*') + + # tests if a resolved version intersects with given range + if intersects(resolve.maya, '2019+') + ... - # tests if a resolved version intersects with given range - if intersects(resolve.maya, '2019+') - ... + # same as above + if intersects(resolve.maya.version, '2019+') + ... - # same as above - if intersects(resolve.maya.version, '2019+') - ... + .. code-block:: python - # disable my cli tools if .foo.cli-0 was specified - def commands(): - if intersects(ephemerals.get('foo.cli', '1'), '1'): - env.PATH.append('{root}/bin') + # disable my cli tools if .foo.cli-0 was specified + def commands(): + if intersects(ephemerals.get('foo.cli', '1'), '1'): + env.PATH.append('{root}/bin') Args: obj (VariantBinding or str): Object to test, either a diff --git a/src/rez/serialise.py b/src/rez/serialise.py index ae0efabfd..5be6f8052 100644 --- a/src/rez/serialise.py +++ b/src/rez/serialise.py @@ -111,17 +111,17 @@ def load_from_file(filepath, format_=FileFormat.py, update_data_callback=None, """Load data from a file. Note: - Any functions from a .py file will be converted to `SourceCode` objects. + Any functions from a .py file will be converted to :class:`.SourceCode` objects. Args: filepath (str): File to load. - format_ (`FileFormat`): Format of file contents. - update_data_callback (callable): Used to change data before it is + format_ (FileFormat): Format of file contents. + update_data_callback (typing.Callable): Used to change data before it is returned or cached. disable_memcache (bool): If True, don't r/w to memcache. Returns: - dict. + dict: """ filepath = os.path.realpath(filepath) cache_filepath = file_cache.get(filepath) @@ -224,10 +224,10 @@ def load_py(stream, filepath=None): """Load python-formatted data from a stream. Args: - stream (file-like object). + stream (typing.IO): Returns: - dict. + dict: """ with add_sys_paths(config.package_definition_build_python_paths): return _load_py(stream, filepath=filepath) @@ -273,7 +273,7 @@ def _load_py(stream, filepath=None): class EarlyThis(object): - """The 'this' object for @early bound functions. + """The ``this`` object for ``@early`` bound functions. Just exposes raw package data as object attributes. """ @@ -298,10 +298,11 @@ def process_python_objects(data, filepath=None): """Replace certain values in the given package data dict. Does things like: - * evaluates @early decorated functions, and replaces with return value; - * converts functions into `SourceCode` instances so they can be serialized + + * evaluates ``@early`` decorated functions, and replaces with return value; + * converts functions into :class:`.SourceCode` instances so they can be serialized out to installed packages, and evaluated later; - * strips some values (modules, __-leading variables) that are never to be + * strips some values (modules, ``__``-leading variables) that are never to be part of installed packages. Returns: @@ -401,10 +402,10 @@ def load_yaml(stream, **kwargs): """Load yaml-formatted data from a stream. Args: - stream (file-like object). + stream (typing.IO): Returns: - dict. + dict: """ # if there's an error parsing the yaml, and you pass yaml.load a string, # it will print lines of context, but will print "" instead of a @@ -430,10 +431,10 @@ def load_txt(stream, **kwargs): """Load text data from a stream. Args: - stream (file-like object). + stream (typing.IO): Returns: - string. + str: """ content = stream.read() return content diff --git a/src/rez/shells.py b/src/rez/shells.py index 5669de8f9..65dd711e5 100644 --- a/src/rez/shells.py +++ b/src/rez/shells.py @@ -27,7 +27,7 @@ def get_shell_types(): """Returns the available shell types: bash, tcsh etc. Returns: - List of str: Shells. + list[str]: Shells. """ from rez.plugin_managers import plugin_manager return list(plugin_manager.get_plugins('shell')) @@ -37,7 +37,7 @@ def get_shell_class(shell=None): """Get the plugin class associated with the given or current shell. Returns: - class: Plugin class for shell. + type[Shell]: Plugin class for shell. """ if not shell: shell = config.default_shell @@ -53,7 +53,7 @@ def create_shell(shell=None, **kwargs): """Returns a Shell of the given or current type. Returns: - `Shell`: Instance of given shell. + Shell: Instance of given shell. """ if not shell: shell = config.default_shell @@ -120,7 +120,9 @@ def startup_capabilities(cls, rcfile=False, norc=False, stdin=False, """ Given a set of options related to shell startup, return the actual options that will be applied. - @returns 4-tuple representing applied value of each option. + + Returns: + tuple: 4-tuple representing applied value of each option. """ raise NotImplementedError @@ -203,8 +205,9 @@ def spawn_shell(self, context_file, tmpdir, rcfile=None, norc=False, pre_command=None, add_rez=True, package_commands_sourced_first=None, **Popen_args): """Spawn a possibly interactive subshell. + Args: - context:_file File that must be sourced in the new shell, this + context_file: File that must be sourced in the new shell, this configures the Rez environment. tmpdir: Tempfiles, if needed, should be created within this path. rcfile: Custom startup script. @@ -230,7 +233,7 @@ def spawn_shell(self, context_file, tmpdir, rcfile=None, norc=False, popen_args: args to pass to the shell process object constructor. Returns: - A subprocess.Popen object representing the shell process. + subprocess.Popen: A subprocess.Popen object representing the shell process. """ raise NotImplementedError @@ -320,8 +323,8 @@ def join(cls, command): class UnixShell(Shell): - """ - A base class for common *nix shells, such as bash and tcsh. + r""" + A base class for common \*nix shells, such as bash and tcsh. """ rcfile_arg = None norc_arg = None @@ -352,6 +355,7 @@ def supports_stdin(cls): def get_startup_sequence(cls, rcfile, norc, stdin, command): """ Return a dict containing: + - 'stdin': resulting stdin setting. - 'command': resulting command setting. - 'do_rcfile': True if a file should be sourced directly. diff --git a/src/rez/solver.py b/src/rez/solver.py index 2ec0f35fd..8f83ee599 100644 --- a/src/rez/solver.py +++ b/src/rez/solver.py @@ -2212,6 +2212,7 @@ def failure_reason(self, failure_index=None): failure_index: Index of the fail to return the graph for (can be negative). If None, the most appropriate failure is chosen according to these rules: + - If the fail is cyclic, the most recent fail (the one containing the cycle) is used; - If a callback has caused a failure, the most recent fail is used; diff --git a/src/rez/suite.py b/src/rez/suite.py index 0ee4a4fac..897c974d9 100644 --- a/src/rez/suite.py +++ b/src/rez/suite.py @@ -39,6 +39,7 @@ class Suite(object): context's tools override tools from other contexts. There are several ways to avoid tool name clashes: + - Hide a tool. This removes it from the suite even if it does not clash; - Prefix/suffix a context. When you do this, all the tools in the context have the prefix/suffix applied; @@ -300,7 +301,8 @@ def get_tools(self): """Get the tools exposed by this suite. Returns: - A dict, keyed by aliased tool name, with dict entries: + dict: A dict, keyed by aliased tool name, with dict entries: + - tool_name (str): The original, non-aliased name of the tool; - tool_alias (str): Aliased tool name (same as key); - context_name (str): Name of the context containing the tool; @@ -354,7 +356,8 @@ def get_hidden_tools(self): Hidden tools are those that have been explicitly hidden via `hide_tool`. Returns: - A list of dicts, where each dict contains: + list[dict]: A list of dicts, where each dict contains: + - tool_name (str): The original, non-aliased name of the tool; - tool_alias (str): Aliased tool name (same as key); - context_name (str): Name of the context containing the tool; diff --git a/src/rez/utils/colorize.py b/src/rez/utils/colorize.py index f20fb28ae..dd62372c5 100644 --- a/src/rez/utils/colorize.py +++ b/src/rez/utils/colorize.py @@ -251,13 +251,11 @@ class ColorizedStreamHandler(logging.StreamHandler): This handler uses the `Colorama`_ module to style the log messages based on the rez configuration. - Attributes: - STYLES (dict): A mapping between the Python logger levels and a function - that can be used to provide the appropriate styling. - .. _Colorama: https://pypi.python.org/pypi/colorama """ + #: A mapping between the Python logger levels and a function that can be used + #: to provide the appropriate styling. STYLES = { 50: critical, 40: error, diff --git a/src/rez/utils/data_utils.py b/src/rez/utils/data_utils.py index c3a8a4ea1..35d0c0a2a 100644 --- a/src/rez/utils/data_utils.py +++ b/src/rez/utils/data_utils.py @@ -50,8 +50,8 @@ class DelayLoad(object): Supported formats: - - yaml (*.yaml, *.yml) - - json (*.json) + - yaml (``*.yaml``, ``*.yml``) + - json (``*.json``) """ def __init__(self, filepath): self.filepath = os.path.expanduser(filepath) @@ -168,7 +168,7 @@ def get_dict_diff(d1, d2): was affected. Returns: - 3-tuple: + tuple: 3-tuple: - list of added keys; - list of removed key; - list of changed keys. @@ -286,6 +286,9 @@ class cached_class_property(object): """ def __init__(self, func, name=None): self.func = func + # Make sure that Sphinx autodoc can follow and get the docstring from our wrapped function. + # TODO: Doesn't work... + functools.update_wrapper(self, func) def __get__(self, instance, owner=None): assert owner diff --git a/src/rez/utils/execution.py b/src/rez/utils/execution.py index a94e1def4..2a3071549 100644 --- a/src/rez/utils/execution.py +++ b/src/rez/utils/execution.py @@ -125,7 +125,7 @@ def create_executable_script(filepath, body, program=None, py_script_mode=None): Args: filepath (str): File to create. - body (str or callable): Contents of the script. If a callable, its code + body (str or typing.Callable): Contents of the script. If a callable, its code is used as the script body. program (str): Name of program to launch the script. Default is 'python' py_script_mode(ExecutableScriptMode): What kind of script to create. diff --git a/src/rez/utils/filesystem.py b/src/rez/utils/filesystem.py index be2906b2d..e9c6ddfaf 100644 --- a/src/rez/utils/filesystem.py +++ b/src/rez/utils/filesystem.py @@ -507,14 +507,14 @@ def to_posixpath(path): def canonical_path(path, platform=None): - """ Resolves symlinks, and formats filepath. + r""" Resolves symlinks, and formats filepath. Resolves symlinks, lowercases if filesystem is case-insensitive, formats filepath using slashes appropriate for platform. Args: path (str): Filepath being formatted - platform (rez.utils.platform_.Platform): Indicates platform path is being + platform (rez.utils.platform\_.Platform): Indicates platform path is being formatted for. Defaults to current platform. Returns: @@ -542,38 +542,38 @@ def encode_filesystem_name(input_str): The rules for the encoding are: - 1) Any lowercase letter, digit, period, or dash (a-z, 0-9, ., or -) is + 1. Any lowercase letter, digit, period, or dash (a-z, 0-9, ., or -) is encoded as-is. - 2) Any underscore is encoded as a double-underscore ("__") + 2. Any underscore is encoded as a double-underscore (``__``) - 3) Any uppercase ascii letter (A-Z) is encoded as an underscore followed + 3. Any uppercase ascii letter (A-Z) is encoded as an underscore followed by the corresponding lowercase letter (ie, "A" => "_a") - 4) All other characters are encoded using their UTF-8 encoded unicode - representation, in the following format: "_NHH..., where: - a) N represents the number of bytes needed for the UTF-8 encoding, - except with N=0 for one-byte representation (the exception for N=1 - is made both because it means that for "standard" ascii characters - in the range 0-127, their encoding will be _0xx, where xx is their - ascii hex code; and because it mirrors the ways UTF-8 encoding - itself works, where the number of bytes needed for the character can - be determined by counting the number of leading "1"s in the binary - representation of the character, except that if it is a 1-byte - sequence, there are 0 leading 1's). - b) HH represents the bytes of the corresponding UTF-8 encoding, in - hexadecimal (using lower-case letters) - - As an example, the character "*", whose (hex) UTF-8 representation - of 2A, would be encoded as "_02a", while the "euro" symbol, which - has a UTF-8 representation of E2 82 AC, would be encoded as - "_3e282ac". (Note that, strictly speaking, the "N" part of the - encoding is redundant information, since it is essentially encoded - in the UTF-8 representation itself, but it makes the resulting - string more human-readable, and easier to decode). - - As an example, the string "Foo_Bar (fun).txt" would get encoded as: - _foo___bar_020_028fun_029.txt + 4. All other characters are encoded using their UTF-8 encoded unicode + representation, in the following format: ``_NHH...``, where: + + * N represents the number of bytes needed for the UTF-8 encoding, + except with N=0 for one-byte representation (the exception for N=1 + is made both because it means that for "standard" ascii characters + in the range 0-127, their encoding will be _0xx, where xx is their + ascii hex code; and because it mirrors the ways UTF-8 encoding + itself works, where the number of bytes needed for the character can + be determined by counting the number of leading "1"s in the binary + representation of the character, except that if it is a 1-byte + sequence, there are 0 leading 1's). + * HH represents the bytes of the corresponding UTF-8 encoding, in + hexadecimal (using lower-case letters) + + As an example, the character ``*``, whose (hex) UTF-8 representation + of 2A, would be encoded as "_02a", while the "euro" symbol, which + has a UTF-8 representation of E2 82 AC, would be encoded as + "_3e282ac". (Note that, strictly speaking, the "N" part of the + encoding is redundant information, since it is essentially encoded + in the UTF-8 representation itself, but it makes the resulting + string more human-readable, and easier to decode). + + As an example, the string "Foo_Bar (fun).txt" would get encoded as ``_foo___bar_020_028fun_029.txt``. """ if isinstance(input_str, six.string_types): input_str = unicode(input_str) diff --git a/src/rez/utils/logging_.py b/src/rez/utils/logging_.py index d601a4a37..201305393 100644 --- a/src/rez/utils/logging_.py +++ b/src/rez/utils/logging_.py @@ -85,7 +85,7 @@ def view_file_logs(globbed_path, loglevel_index=None): Prints to stdout. Args: - globbed_path (str): Logfiles, eg '/foo/logs/*.log' + globbed_path (str): Logfiles, eg ``/foo/logs/*.log`` loglevel_index (int): Position on each log line where log level (INFO etc) is expected. This is used for colorisation only, and if None, no colors are applied. diff --git a/src/rez/utils/memcached.py b/src/rez/utils/memcached.py index 52fc54bf4..d66108578 100644 --- a/src/rez/utils/memcached.py +++ b/src/rez/utils/memcached.py @@ -283,7 +283,9 @@ def memcached(servers, key=None, from_cache=None, to_cache=None, time=0, being returned. If you do not want a result to be cached, wrap the return value of your function in a `DoNotCache` object. - Example: + Examples: + + .. code-block:: python @memcached('127.0.0.1:11211') def _listdir(path): @@ -300,11 +302,11 @@ def _listdir(path): Args: servers (str or list of str): memcached server uri(s), eg '127.0.0.1:11211'. This arg can be None also, in which case memcaching is disabled. - key (callable, optional): Function that, given the target function's args, + key (typing.Optional[typing.Callable]): Function that, given the target function's args, returns the string key to use in memcached. - from_cache (callable, optional): If provided, and a cache hit occurs, the + from_cache (typing.Optional[typing.Callable]): If provided, and a cache hit occurs, the cached value will be translated by this function before being returned. - to_cache (callable, optional): If provided, and a cache miss occurs, the + to_cache (typing.Optional[typing.Callable]): If provided, and a cache miss occurs, the function's return value will be translated by this function before being cached. time (int): Tells memcached the time which this value should expire, either diff --git a/src/rez/utils/pip.py b/src/rez/utils/pip.py index beeeddca1..2df33c137 100644 --- a/src/rez/utils/pip.py +++ b/src/rez/utils/pip.py @@ -49,23 +49,23 @@ def pip_to_rez_version(dist_version, allow_legacy=True): The python version schema specification isn't 100% compatible with rez. - 1: version epochs (they make no sense to rez, so they'd just get stripped - of the leading N!; - 2: python versions are case insensitive, so they should probably be + 1. version epochs (they make no sense to rez, so they'd just get stripped + of the leading ``N!``; + 2. python versions are case insensitive, so they should probably be lowercased when converted to a rez version. - 3: local versions are also not compatible with rez + 3. local versions are also not compatible with rez The canonical public version identifiers MUST comply with the following scheme: - [N!]N(.N)*[{a|b|rc}N][.postN][.devN] + ``[N!]N(.N)*[{a|b|rc}N][.postN][.devN]`` - Epoch segment: N! - skip - Release segment: N(.N)* 0 as is - Pre-release segment: {a|b|c|rc|alpha|beta|pre|preview}N - always lowercase - Post-release segment: .{post|rev|r}N - always lowercase - Development release segment: .devN - always lowercase + Epoch segment: ``N!`` - skip + Release segment: N(.N)* 0`` as is + Pre-release segment: ``{a|b|c|rc|alpha|beta|pre|preview}N`` - always lowercase + Post-release segment: ``.{post|rev|r}N`` - always lowercase + Development release segment: ``.devN`` - always lowercase Local version identifiers MUST comply with the following scheme: - [+] - use - instead of + + ``[+]`` - use - instead of + Args: dist_version (str): The distribution version to be converted. @@ -76,7 +76,7 @@ def pip_to_rez_version(dist_version, allow_legacy=True): Raises: InvalidVersion: When legacy mode is not allowed and a PEP440 - incompatible version is detected. + incompatible version is detected. .. _PEP 440 (all possible matches): https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions @@ -164,20 +164,22 @@ def pip_specifier_to_rez_requirement(specifier): Example conversions: - | PEP440 | rez | - |-------------|-------------| - | ==1 | 1+<1.1 | - | ==1.* | 1 | - | >1 | 1.1+ | - | <1 | <1 | - | >=1 | 1+ | - | <=1 | <1.1 | - | ~=1.2 | 1.2+<2 | - | ~=1.2.3 | 1.2.3+<1.3 | - | !=1 | <1|1.1+ | - | !=1.2 | <1.2|1.2.1+ | - | !=1.* | <1|2+ | - | !=1.2.* | <1.2|1.3+ | + ============== =============== + PEP440 rez + ============== =============== + ``==1`` ``1+<1.1`` + ``==1.*`` ``1`` + ``>1`` ``1.1+`` + ``<1`` ``<1`` + ``>=1`` ``1+`` + ``<=1`` ``<1.1`` + ``~=1.2`` ``1.2+<2`` + ``~=1.2.3`` ``1.2.3+<1.3`` + ``!=1`` ``<1|1.1+`` + ``!=1.2`` ``<1.2|1.2.1+`` + ``!=1.*`` ``<1|2+`` + ``!=1.2.*`` ``<1.2|1.3+`` + ============== =============== Args: specifier (`package.SpecifierSet`): Pip specifier. @@ -353,14 +355,16 @@ def get_rez_requirements(installed_dist, python_version, name_casings=None): Example result: - { - "requires": ["foo-1.2+<2"], - "variant_requires": ["future", "python-2.7"], - "metadata": { - # metadata pertinent to rez - ... - } - } + .. code-block:: python + + { + "requires": ["foo-1.2+<2"], + "variant_requires": ["future", "python-2.7"], + "metadata": { + # metadata pertinent to rez + ... + } + } Each requirement has had its package name converted to the rez equivalent. The 'variant_requires' key contains requirements specific to the current diff --git a/src/rez/utils/platform_mapped.py b/src/rez/utils/platform_mapped.py index ce7c7f01b..aeb37dc28 100644 --- a/src/rez/utils/platform_mapped.py +++ b/src/rez/utils/platform_mapped.py @@ -14,16 +14,18 @@ def platform_mapped(func): regular expression is being used. For example: - config.platform_map = { - "os": { - r"Scientific Linux-(.*)": r"Scientific-\1", # Scientific Linux-x.x -> Scientific-x.x - r"Ubuntu-14.\d": r"Ubuntu-14", # Any Ubuntu-14.x -> Ubuntu-14 - }, - "arch": { - "x86_64": "64bit", # Maps both x86_64 and amd64 -> 64bit (don't) - "amd64": "64bit", - }, - } + .. code-block:: python + + config.platform_map = { + "os": { + r"Scientific Linux-(.*)": r"Scientific-\1", # Scientific Linux-x.x -> Scientific-x.x + r"Ubuntu-14.\d": r"Ubuntu-14", # Any Ubuntu-14.x -> Ubuntu-14 + }, + "arch": { + "x86_64": "64bit", # Maps both x86_64 and amd64 -> 64bit (don't) + "amd64": "64bit", + }, + } """ def inner(*args, **kwargs): diff --git a/src/rez/utils/py_dist.py b/src/rez/utils/py_dist.py index 904a37870..4bdc0e909 100644 --- a/src/rez/utils/py_dist.py +++ b/src/rez/utils/py_dist.py @@ -134,6 +134,7 @@ def convert_dist(name, dest_path, make_variant=True, ignore_dirs=None, ignore_dirs (bool): List of directory names to not copy from the dist. python_requirement (str): How the package should depend on python. One of: + - "major": depend on python-X - "major_minor": depend on python-X.X - any other value: this string is used as the literal version diff --git a/src/rez/utils/resources.py b/src/rez/utils/resources.py index 2fca65e7a..489004eda 100644 --- a/src/rez/utils/resources.py +++ b/src/rez/utils/resources.py @@ -67,16 +67,14 @@ class Resource(six.with_metaclass(LazyAttributeMeta, object)): Note: You can access the entire validated resource data dict using the `validated_data` function, and test full validation using `validate_data`. - - Attributes: - key (str): Unique identifier of the resource type. - schema (Schema): Schema for the resource data. Must validate a dict. - Can be None, in which case the resource does not load any data. - schema_error (Exception): The exception type to raise on key - validation failure. """ + #: Unique identifier of the resource type. key = None + #: Schema for the resource data. + #: Must validate a dict. Can be None, in which case the resource does + #: not load any data. schema = None + #: The exception type to raise on key validation failure. schema_error = Exception @classmethod diff --git a/src/rez/utils/schema.py b/src/rez/utils/schema.py index a0d23b441..5dc6671dd 100644 --- a/src/rez/utils/schema.py +++ b/src/rez/utils/schema.py @@ -23,10 +23,12 @@ def schema_keys(schema): Non-string keys are ignored. Returns: - Set of string keys of a schema which is in the form (eg): + set[str]: Set of string keys of a schema which is in the form (eg): - schema = Schema({Required("foo"): int, - Optional("bah"): basestring}) + .. code-block:: python + + schema = Schema({Required("foo"): int, + Optional("bah"): basestring}) """ def _get_leaf(value): if isinstance(value, Schema): @@ -50,9 +52,9 @@ def dict_to_schema(schema_dict, required, allow_custom_keys=True, modifier=None) Args: required (bool): Whether to make schema keys optional or required. - allow_custom_keys (bool, optional): If True, creates a schema that + allow_custom_keys (typing.Optional[bool]): If True, creates a schema that allows custom items in dicts. - modifier (callable): Functor to apply to dict values - it is applied + modifier (typing.Optional[typing.Callable]): Functor to apply to dict values - it is applied via `Schema.Use`. Returns: