Skip to content

Commit

Permalink
Merge pull request #15 from anaconda-distribution/smartin_clean_docs
Browse files Browse the repository at this point in the history
Auto-formats Sphinx docs
  • Loading branch information
schuylermartin45 authored Nov 10, 2023
2 parents da8a5af + 877e06b commit 9ee5333
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 78 deletions.
19 changes: 19 additions & 0 deletions .docconvert.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"input_style": "guess",
"output_style": "rest",
"accepted_shebangs": [
"python"
],
"output": {
"first_line": false,
"replace_quotes": "\"\"\"",
"standard_indent": " ",
"tab_length": 4,
"realign": true,
"max_line_length": 120,
"use_optional": false,
"remove_type_back_ticks": "true",
"use_types": false,
"separate_keywords": false
}
}
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ CONDA_ACTIVATE = source $$(conda info --base)/etc/profile.d/conda.sh ; conda act
# Ensure that we are using the python interpretter provided by the conda environment.
PYTHON3 := "$(CONDA_PREFIX)/bin/python3"

.PHONY: clean clean-cov clean-build clean-env clean-pyc clean-test help pre-commit test test-cov lint format analyze
.PHONY: clean clean-cov clean-build clean-env clean-pyc clean-test help pre-commit test test-cov lint format format-docs analyze
.DEFAULT_GOAL := help

CONDA_ENV_NAME ?= anaconda-packaging-utils
Expand Down Expand Up @@ -107,5 +107,8 @@ format: ## runs the code auto-formatter
isort --profile black --line-length=120 anaconda_packaging_utils
black --line-length=120 anaconda_packaging_utils

format-docs: ## runs the docstring auto-formatter. Note this requires manually installing `docconvert`
docconvert --in-place --config .docconvert.json anaconda_packaging_utils

analyze: ## runs static analyzer on the project
mypy --config-file=.mypy.ini anaconda_packaging_utils/
mypy --config-file=.mypy.ini --cache-dir=/dev/null anaconda_packaging_utils/
26 changes: 13 additions & 13 deletions anaconda_packaging_utils/api/github_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from github import Github, Repository

# TODO enforce type checking when `percy` exports types
from percy.render.recipe import Recipe # type: ignore[import]
from percy.render.recipe import Recipe

import anaconda_packaging_utils.cryptography.utils as crypto_utils
from anaconda_packaging_utils.storage import file_io
Expand Down Expand Up @@ -60,7 +60,7 @@ class GitHubApi:
def __init__(self) -> None:
"""
Constructs a GitHubApi Instance
:raises ApiException: If there was a failure to authenticate.
:raises ApiException: If there was a failure to authenticate.
"""
if len(GitHubApi._gh) == 0:
try:
Expand All @@ -71,8 +71,8 @@ def __init__(self) -> None:
def fetch_aggregate(self) -> Repository.Repository:
"""
Convenience function for accessing the `aggregate` repo.
:raises ApiException: If there was a failure to access the repo.
:return: Repository object that represents `aggregate`.
:raises ApiException: If there was a failure to access the repo.
:returns: Repository object that represents `aggregate`.
"""
try:
return GitHubApi._gh[0].get_repo(REPO_AGGREGATE_PATH)
Expand All @@ -82,10 +82,10 @@ def fetch_aggregate(self) -> Repository.Repository:
def fetch_feedstock(self, package: str) -> tuple[Repository.Repository, Optional[str]]:
"""
Convenience function for accessing a feedstock repository.
:param package: Name of the target package.
:raises ApiException: If there was a failure to access the repo.
:return: Repository object that represents the target package feedstock AND if possible, the SHA-1 hash of the
version of the repo set in `aggregate`.
:param package: Name of the target package.
:raises ApiException: If there was a failure to access the repo.
:returns: Repository object that represents the target package feedstock AND if possible, the SHA-1 hash of the
version of the repo set in `aggregate`.
"""
# Treat `aggregate` as the initial source of truth. As `aggregate` "should" be what's publicly available, we
# should use it as a basis of our target feedstock version.
Expand Down Expand Up @@ -120,12 +120,12 @@ def fetch_feedstock(self, package: str) -> tuple[Repository.Repository, Optional
except Exception as e:
raise ApiException(f"Failed to access `{feedstock_name}` from aggregate") from e

def fetch_recipe(self, package: str) -> Recipe: # type: ignore[no-any-unimported]
def fetch_recipe(self, package: str) -> Recipe:
"""
Pulls a recipe file down for use with `percy`.
:param package: Name of the target package.
:raises ApiException: If there was a failure to access the repo.
:return: Recipe, as a Percy object.
:param package: Name of the target package.
:raises ApiException: If there was a failure to access the repo.
:returns: Recipe, as a Percy object.
"""
feedstock, sha = self.fetch_feedstock(package)
# Render-safe for the API, default to empty string
Expand All @@ -143,4 +143,4 @@ def fetch_recipe(self, package: str) -> Recipe: # type: ignore[no-any-unimporte
tag=f"{package}_recipe",
)
log.info("Recipe for `%s` downloaded to: %s", package, tmp)
return Recipe.from_file(tmp) # type: ignore[misc]
return Recipe.from_file(tmp) # type: ignore[arg-type, misc]
51 changes: 25 additions & 26 deletions anaconda_packaging_utils/api/pypi_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class VersionMetadata:
def get_schema() -> SchemaType:
"""
Returns a JSON schema used to validate JSON responses.
:return: JSON schema for a packaging info
:returns: JSON schema for a packaging info
"""
return {
"type": "object",
Expand Down Expand Up @@ -114,10 +114,9 @@ class PackageInfo:
def get_schema(requires_releases: bool) -> SchemaType:
"""
Returns a JSON schema used to validate JSON responses.
:param requires_releases: Depending on the endpoint used, the API will optionally return information about
every release/package version. Setting this to "True" will require the `releases`
property
:return: JSON schema for a packaging info
:param requires_releases: Depending on the endpoint used, the API will optionally return information about every
release/package version. Setting this to "True" will require the `releases` property
:returns: JSON schema for a packaging info
"""
base: SchemaType = {
"type": "object",
Expand Down Expand Up @@ -218,7 +217,7 @@ def _calc_package_metadata_url(package: str) -> str:
"""
Generates the URL for fetching package metadata
:param package: Name of the package
:return: REST endpoint to use to fetch package metadata
:returns: REST endpoint to use to fetch package metadata
"""
return f"{BASE_URL}/{package}/json"

Expand All @@ -228,17 +227,17 @@ def _calc_package_version_metadata_url(package: str, version: str) -> str:
Generates the URL for fetching package metadata, at a specific version
:param package: Name of the package
:param version: Version of the package
:return: REST endpoint to use to fetch package metadata
:returns: REST endpoint to use to fetch package metadata
"""
return f"{BASE_URL}/{package}/{version}/json"


def _make_request_and_validate(endpoint: str, schema: SchemaType) -> JsonType:
"""
Makes an HTTP request against the API and validates the result.
:param endpoint: REST endpoint (URL) used in this request
:param schema: JSON schema to validate the results against
:raises ApiException: If there is an unrecoverable issue with the API
:param endpoint: REST endpoint (URL) used in this request
:param schema: JSON schema to validate the results against
:raises ApiException: If there is an unrecoverable issue with the API
"""
response = None
try:
Expand Down Expand Up @@ -280,9 +279,9 @@ def _make_request_and_validate(endpoint: str, schema: SchemaType) -> JsonType:
def _check_for_empty_field(field: str, value: str | None) -> None:
"""
Convenience function that checks if a critical field is empty/null
:param field: Field name (for debugging purposes)
:param value: Value of the field to check
:raises ApiException: If the field is empty
:param field: Field name (for debugging purposes)
:param value: Value of the field to check
:raises ApiException: If the field is empty
"""
if value is None or len(value) == 0:
raise ApiException(f"`{field}` field is empty: {value}")
Expand All @@ -291,10 +290,10 @@ def _check_for_empty_field(field: str, value: str | None) -> None:
def _parse_version_metadata(data: JsonType) -> VersionMetadata:
"""
Given a schema-validated JSON, parse version metadata
:param data: JSON data to parse. Pre-req: This must have been previously validated against the schema
provided by the class.
:raises ApiException: If there is an unrecoverable issue with the API
:return: Version metadata, as an immutable dataclass object
:param data: JSON data to parse. Pre-req: This must have been previously validated against the schema provided by
the class.
:raises ApiException: If there is an unrecoverable issue with the API
:returns: Version metadata, as an immutable dataclass object
"""
# Validate non-string fields
time_str: Final[str] = data["upload_time_iso_8601"] # type: ignore
Expand Down Expand Up @@ -335,10 +334,10 @@ def _parse_version_metadata(data: JsonType) -> VersionMetadata:
def _parse_package_info(data: JsonType) -> PackageInfo:
"""
Given a schema-validated JSON, parse version metadata
:param data: JSON data to parse. Pre-req: This must have been previously validated against the schema
provided by the class.
:raises ApiException: If there is an unrecoverable issue with the API
:return: Package info, as an immutable dataclass object
:param data: JSON data to parse. Pre-req: This must have been previously validated against the schema provided by
the class.
:raises ApiException: If there is an unrecoverable issue with the API
:returns: Package info, as an immutable dataclass object
"""
# Extract the VersionMetadata for "source" objects
version_metadata: VersionMetadata | None = None
Expand Down Expand Up @@ -396,8 +395,8 @@ def _parse_package_info(data: JsonType) -> PackageInfo:
def fetch_package_metadata(package: str) -> PackageMetadata:
"""
Fetches and validates package metadata from the PyPi API.
:param package: Name of the package
:raises ApiException: If there is an unrecoverable issue with the API
:param package: Name of the package
:raises ApiException: If there is an unrecoverable issue with the API
"""
response_json = _make_request_and_validate(
_calc_package_metadata_url(package),
Expand Down Expand Up @@ -451,9 +450,9 @@ def fetch_package_metadata(package: str) -> PackageMetadata:
def fetch_package_version_metadata(package: str, version: str) -> PackageMetadata:
"""
Fetches and validates package metadata (at a specific version) from the PyPi API.
:param package: Name of the package
:param version: Version of the package
:raises ApiException: If there is an unrecoverable issue with the API
:param package: Name of the package
:param version: Version of the package
:raises ApiException: If there is an unrecoverable issue with the API
"""
response_json = _make_request_and_validate(
_calc_package_version_metadata_url(package, version),
Expand Down
35 changes: 17 additions & 18 deletions anaconda_packaging_utils/cli/subshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
def run_shell(cmd: str, capture_output: bool = True, cwd: str = "") -> subprocess.CompletedProcess[str]:
"""
Utility wrapper around `subprocess.run()` that provides the ability to run shell commands outside of Python.
:param cmd: Command to execute
:param capture_output: (Optional) Indicates if the output is directly printed to the console or if it is buffered
and put into a string. If the called command prompts for input that can't be suppressed
(like a password) this flag should be set to `False`
:param cwd: (Optional) "Current Working Directory", changes the path that the shell operates in.
Effectively acts as if you ran `cd` prior to the command.
:return: Process object including a POSIX-style return code
:param cmd: Command to execute
:param capture_output: (Optional) Indicates if the output is directly printed to the console or if it is buffered
and put into a string. If the called command prompts for input that can't be suppressed (like a password) this
flag should be set to `False`
:param cwd: (Optional) "Current Working Directory", changes the path that the shell operates in. Effectively acts as
if you ran `cd` prior to the command.
:returns: Process object including a POSIX-style return code
"""
output = subprocess.run(
cmd,
Expand Down Expand Up @@ -46,17 +46,16 @@ def run_shell_chain(
"""
Runs a series of shell commands in a chain. If one command fails, the whole chain fails and execution stops. Note:
`cd` commands are specially processed and run with the "Current Working Directory" (cwd) feature of `subprocess.run`
:param cmds: List of commands to execute in a sequence
:param capture_output: (Optional) Indicates if the output is directly printed to the console or if it is buffered
and put into a string. If the called command prompts for input that can't be suppressed
(like a password) this flag should be set to `False`
:param is_fatal_error: (Optional) Stops chain execution on first command that returns a non-zero return code. The
calling program can determine which command failed by looking at the length of the returned
list and/or by looking at the `args` member of last-returned completed process object.
:param cwd: (Optional) "Current Working Directory", changes the path that the shell operates in.
Effectively acts as if you ran `cd` prior to the command. Note that if subsequent `cd`
commands are used, this value is overwritten.
:return: Process object including a POSIX-style return code
:param cmds: List of commands to execute in a sequence
:param capture_output: (Optional) Indicates if the output is directly printed to the console or if it is buffered
and put into a string. If the called command prompts for input that can't be suppressed (like a password) this
flag should be set to `False`
:param is_fatal_error: (Optional) Stops chain execution on first command that returns a non-zero return code. The
calling program can determine which command failed by looking at the length of the returned list and/or by
looking at the `args` member of last-returned completed process object.
:param cwd: (Optional) "Current Working Directory", changes the path that the shell operates in. Effectively acts as
if you ran `cd` prior to the command. Note that if subsequent `cd` commands are used, this value is overwritten.
:returns: Process object including a POSIX-style return code
"""
completed_procs = []
for cmd in cmds:
Expand Down
20 changes: 10 additions & 10 deletions anaconda_packaging_utils/cryptography/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,43 @@
def is_valid_hex(s: str) -> bool:
"""
Checks if a string is a valid hex string
:param s: String to validate
:return: True if the string is a valid hex string. False otherwise.
:param s: String to validate
:returns: True if the string is a valid hex string. False otherwise.
"""
return all(c in string.hexdigits for c in s)


def is_valid_md5(s: str) -> bool:
"""
Checks if a string is a valid MD5 hash
:param s: String to validate
:return: True if the string is a valid MD5 hash. False otherwise.
:param s: String to validate
:returns: True if the string is a valid MD5 hash. False otherwise.
"""
return len(s) == 32 and is_valid_hex(s)


def is_valid_sha256(s: str) -> bool:
"""
Checks if a string is a valid SHA-256 hash
:param s: String to validate
:return: True if the string is a valid SHA-256 hash. False otherwise.
:param s: String to validate
:returns: True if the string is a valid SHA-256 hash. False otherwise.
"""
return len(s) == 64 and is_valid_hex(s)


def is_valid_sha1(s: str) -> bool:
"""
Checks if a string is a valid SHA-1 hash. This is used by git/GitHub.
:param s: String to validate
:return: True if the string is a valid SHA-1 hash. False otherwise.
:param s: String to validate
:returns: True if the string is a valid SHA-1 hash. False otherwise.
"""
return len(s) == 40 and is_valid_hex(s)


def cast_hex_str_to_int(s: str) -> int:
"""
Converts a hex string to an integer
:param s: String to convert. This must be a valid hex-string.
:return: Integer equivalent of a hex string.
:param s: String to convert. This must be a valid hex-string.
:returns: Integer equivalent of a hex string.
"""
return int(s, 16)
12 changes: 6 additions & 6 deletions anaconda_packaging_utils/storage/config_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _generate_config_schema() -> SchemaType:
`types-jsonschema` defines their schema type with an `Any`, and that upsets `mypy` to the point where it'll throw
multiple errors on almost every line of the schema definition.
:return: Config Data Schema
:returns: Config Data Schema
"""
return {
"type": "object",
Expand Down Expand Up @@ -109,25 +109,25 @@ def __init__(self, file_path: str | Path = CONFIG_FILE_DEFAULT_PATH) -> None:
def __str__(self) -> str:
"""
Pretty-prints the configuration data key-value map as a string. Use only for debugging purposes.
:return: Pretty-printed version of the key-value table.
:returns: Pretty-printed version of the key-value table.
"""
with ConfigData._mutex:
return json.dumps(ConfigData._config_tbl, indent=2)

def __contains__(self, key: str) -> bool:
"""
Returns true if a key-path is found.
:return: True if the key-path is found. False otherwise.
:returns: True if the key-path is found. False otherwise.
"""
with ConfigData._mutex:
return key in ConfigData._config_tbl

def __getitem__(self, key: str) -> str:
"""
Returns a value by a key-path.
:param key: Full key that describes a value
:raises KeyError: If the key is not found
:return: The value found at the provided key
:param key: Full key that describes a value
:raises KeyError: If the key is not found
:returns: The value found at the provided key
"""
with ConfigData._mutex:
return ConfigData._config_tbl[key]
6 changes: 3 additions & 3 deletions anaconda_packaging_utils/storage/file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
def write_file(file: Path | str, content: str | list[str]) -> None:
"""
Writes text to a file
:param file: File name/path to file to write
:param file: File name/path to file to write
:param content: String (or list of strings) to write to a file. If a list is given, strings are written line-by-line
"""
# Ensure `path` is always a path.
Expand All @@ -38,8 +38,8 @@ def write_temp_file(content: str | list[str], tag: str = "") -> Path:
BE AWARE of the security implications of this.
:param content: String (or list of strings) to write to a file. If a list is given, strings are written line-by-line
:param tag: (Optional) Tag to further help identify the temporary file
:return: Path to the temp file that was written to
:param tag: (Optional) Tag to further help identify the temporary file
:returns: Path to the temp file that was written to
"""
# Naming the file with some origin information allows others to point fingers when we don't clean-up after ourselves
if tag:
Expand Down

0 comments on commit 9ee5333

Please sign in to comment.