Skip to content

Commit

Permalink
Documentation fixes.
Browse files Browse the repository at this point in the history
Removed some warning about autoapi documentation, and some about
possible classes mixes.

Still the warning about PIPE import in TYPE_CHECKING could not be resolved.

Documentation fixes.

Removed some warning about autoapi documentation, and some about
possible classes mixes.

Still the warning about PIPE import in TYPE_CHECKING could not be resolved.

SLSA provenance tests on Windows.

The checksums are different on windows and linux.
New checksums (windows) have been tested with cygwin commands.

Create test files for hash method in binary mode.
  • Loading branch information
grouigrokon committed Oct 19, 2023
1 parent dc96a04 commit a0efe5c
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 63 deletions.
13 changes: 8 additions & 5 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
templates_path = ["_templates"]
autoapi_template_dir = "source/autoapi_templates"

# Remove warnings for auto API template.
exclude_patterns = ["autoapi_templates/index.rst"]

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
Expand All @@ -52,17 +55,17 @@

# General information about the project.
project = "e3-core"
copyright = "2017, AdaCore" # noqa: A001
project_copyright = "2017, AdaCore"
author = "AdaCore"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = "21.0"
version = "24.0"
# The full version, including alpha/beta/rc tags.
release = "21.0"
release = "24.0"

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
Expand All @@ -75,11 +78,11 @@
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]


# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# html_static_path = ["_static"]
html_static_path = []


# -- Options for HTMLHelp output ------------------------------------------
Expand Down
11 changes: 10 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@
# There are some backward incompatible checks in typeguard 3.x
"typeguard<3.0.0",
],
"test": ["mock", "pytest-html", "pytest-socket", "ansi2html", "httpretty"],
"test": [
"mock",
"pytest",
"pytest-html",
"pytest-socket",
"ansi2html",
"httpretty",
],
}

for p in ("darwin", "linux", "linux2", "win32"):
Expand Down Expand Up @@ -54,6 +61,8 @@
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Build Tools",
],
packages=find_packages(where="src"),
Expand Down
8 changes: 5 additions & 3 deletions src/e3/job/walk.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
class Walk:
"""An abstract class scheduling and executing a DAG of actions.
.. |ReturnValue| replace:: :class:`~e3.anod.status.ReturnValue`
:ivar actions: DAG of actions to perform.
:vartype actions: DAG
:ivar prev_fingerprints: A dict of e3.fingerprint.Fingerprint objects,
Expand All @@ -35,13 +37,13 @@ class Walk:
(with the job corresponding to a given entry in the DAG of
actions).
:vartype new_fingerprints: dict[str, Fingerprint | None]
:ivar job_status: A dictionary of job status (ReturnValue), indexed by
:ivar job_status: A dictionary of job status (|ReturnValue|), indexed by
job unique IDs.
:vartype job_status: dict[str, ReturnValue]
:vartype job_status: dict[str, |ReturnValue|]
:ivar scheduler: The scheduler used to schedule and execute all
the actions.
:vartype scheduler: e3.job.scheduler.Scheduler
"""
""" # noqa RST304

def __init__(self, actions: DAG):
"""Object initializer.
Expand Down
146 changes: 98 additions & 48 deletions src/e3/slsa/provenance.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""SLAS provenance package.
"""SLSA provenance package.
.. |DigestAlgorithm| replace:: :class:`DigestAlgorithm`
.. |DigestSet| replace:: :class:`DigestSet`
Expand Down Expand Up @@ -143,7 +143,7 @@ def to_json(self) -> dict:
:return: A |dict| representing the JSON representation of this builder.
""" # noqa RST304
return {
self.ATTR_BUILD_ID: str(self.id),
self.ATTR_BUILD_ID: self.id.to_json(),
self.ATTR_BUILDER_DEPENDENCIES: [
desc.to_json() for desc in self.builderDependencies
],
Expand Down Expand Up @@ -202,8 +202,8 @@ def to_json(self) -> dict:
"""Get the JSON representation of this build metadata."""
return {
self.ATTR_INVOCATION_ID: self.invocationId,
self.ATTR_STARTED_ON: str(self.startedOn),
self.ATTR_FINISHED_ON: str(self.finishedOn),
self.ATTR_STARTED_ON: self.startedOn,
self.ATTR_FINISHED_ON: self.finishedOn,
}


Expand All @@ -228,6 +228,15 @@ class DigestAlgorithm(Enum):
SHAKE256: str = "shake256"
SM3: str = "sm3"

def to_json(self) -> str:
"""Get the JSON representation of this digest algorithm.
The return value is the name of the algorithm.
:return: This digest algorithm name value.
"""
return self.value


class DigestSet(dict[str, str]):
"""Digest set object.
Expand Down Expand Up @@ -310,25 +319,32 @@ def dirHash(path: Path, algorithm: DigestAlgorithm) -> str:
f"Unsupported digest algorithm {algorithm} for DigestSet.dirHash()"
)

dir_hash = getattr(hashlib, str(algorithm.value))()
dir_hash = getattr(hashlib, algorithm.to_json())()

# List files in path, and read them 4096 bytes per 4096 bytes to
# compute a global has.
for filepath in sorted(path.rglob("*")):
if filepath.is_file():
file_hash = getattr(hashlib, str(algorithm.value))()
file_hash = getattr(hashlib, algorithm.to_json())()
with filepath.open("rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
file_hash.update(chunk)

dir_hash.update(
f"{file_hash.hexdigest()} {filepath.relative_to(path)}\n".encode(
"utf-8"
)
)
rel_path: str = f"{filepath.relative_to(path).as_posix()}"
hash_str: str = f"{file_hash.hexdigest()} {rel_path}\n"
dir_hash.update(hash_str.encode("utf-8"))

return f"{dir_hash.hexdigest()}"

def to_json(self) -> dict[str, str]:
"""Get the JSON representation of this digest set.
The JSON representation of a digest set, is the digest set itself.
:return: This digest set.
"""
return self


class Statement(object):
"""SLSA statement object.
Expand Down Expand Up @@ -382,6 +398,21 @@ def _type(self) -> TypeURI:
"""
return self.__type

@property
def predicate(self) -> Predicate | None:
"""Additional parameters of the |Predicate|.
Unset is treated the same as set-but-empty. **MAY** be omitted if
|predicateType| fully describes the predicate.
""" # noqa RST304
return self.__predicate

# noinspection PyPep8Naming
@property
def preticateType(self) -> TypeURI:
"""URI identifying the type of the |Predicate|.""" # noqa RST304
return self.__predicate_type

@property
def subject(self) -> list[ResourceDescriptor]:
"""Set of software artifacts that the attestation applies to.
Expand Down Expand Up @@ -411,20 +442,14 @@ def subject(self) -> list[ResourceDescriptor]:
""" # noqa RST304
return self.__subject

# noinspection PyPep8Naming
@property
def preticateType(self) -> TypeURI:
"""URI identifying the type of the |Predicate|.""" # noqa RST304
return self.__predicate_type

@property
def predicate(self) -> Predicate | None:
"""Additional parameters of the |Predicate|.
Unset is treated the same as set-but-empty. **MAY** be omitted if
|predicateType| fully describes the predicate.
""" # noqa RST304
return self.__predicate
def to_json(self) -> dict:
"""Get the JSON representation of this statement."""
return {
self.ATTR_TYPE: self._type.to_json(),
self.ATTR_SUBJECT: [subject.to_json() for subject in self.subject],
self.ATTR_PREDICATE_TYPE: self.preticateType.to_json(),
self.ATTR_PREDICATE: self.predicate.to_json() if self.predicate else None,
}


class ResourceDescriptor(object):
Expand All @@ -437,6 +462,7 @@ class ResourceDescriptor(object):
.. |is_valid| replace:: :attr:`~ResourceDescriptor.is_valid`
.. |mediaType| replace:: :attr:`~ResourceDescriptor.mediaType`
.. |name| replace:: :attr:`~ResourceDescriptor.name`
.. |to_json| replace:: :meth:`~ResourceDescriptor.to_json`
.. |uri| replace:: :attr:`~ResourceDescriptor.uri`
A size-efficient description of any software artifact or resource (mutable
Expand All @@ -461,25 +487,28 @@ class ResourceDescriptor(object):
of |uri|, |digest|, or |content| specified here.
""" # noqa RST304

__ATTR_ANNOTATIONS: str = "annotations"
__ATTR_CONTENT: str = "content"
__ATTR_DIGEST: str = "digest"
__ATTR_DOWNLOAD_LOCATION: str = "downloadLocation"
__ATTR_MEDIA_TYPE: str = "mediaType"
__ATTR_NAME: str = "name"
__ATTR_URI: str = "uri"
ATTR_ANNOTATIONS: str = "annotations"
ATTR_CONTENT: str = "content"
ATTR_DIGEST: str = "digest"
ATTR_DOWNLOAD_LOCATION: str = "downloadLocation"
ATTR_MEDIA_TYPE: str = "mediaType"
ATTR_NAME: str = "name"
ATTR_URI: str = "uri"

# Order of attributes is taken out of the schema at
# https://slsa.dev/spec/v1.0/provenance
__ATTRIBUTES: tuple = (
__ATTR_URI,
__ATTR_DIGEST,
__ATTR_NAME,
__ATTR_DOWNLOAD_LOCATION,
__ATTR_MEDIA_TYPE,
__ATTR_CONTENT,
__ATTR_ANNOTATIONS,
ATTRIBUTES: tuple = (
ATTR_URI,
ATTR_DIGEST,
ATTR_NAME,
ATTR_DOWNLOAD_LOCATION,
ATTR_MEDIA_TYPE,
ATTR_CONTENT,
ATTR_ANNOTATIONS,
)
"""JSON attributes returned by the |to_json| method (if the given attribute
defines a value).
""" # noqa RST304

# noinspection PyPep8Naming
def __init__(
Expand Down Expand Up @@ -715,16 +744,17 @@ def to_json(self) -> dict:
)
resource_id_dict: dict[str, Any] = {}

for attribute in self.__ATTRIBUTES:
for attribute in self.ATTRIBUTES:
# Special case for content which should be base64 encoded.
value = getattr(self, attribute)
if value and attribute == self.__ATTR_CONTENT:
if value and attribute == self.ATTR_CONTENT:
resource_id_dict[attribute] = base64.b64encode(value).decode("utf-8")
elif value and attribute in (
self.__ATTR_URI,
self.__ATTR_DOWNLOAD_LOCATION,
self.ATTR_DIGEST,
self.ATTR_DOWNLOAD_LOCATION,
self.ATTR_URI,
):
resource_id_dict[attribute] = str(value)
resource_id_dict[attribute] = value.to_json()
elif value:
resource_id_dict[attribute] = value

Expand All @@ -741,10 +771,10 @@ def from_dict(self, initializer: dict[str, Any]) -> None:
.. seealso:: |is_valid|
""" # noqa RST304
for attribute in self.__ATTRIBUTES:
for attribute in self.ATTRIBUTES:
# Special case for content which should be base64 encoded.
value = initializer.get(attribute, None)
if value and attribute == self.__ATTR_CONTENT:
if value and attribute == self.ATTR_CONTENT:
setattr(self, attribute, base64.b64decode(value))
elif value:
setattr(self, attribute, value)
Expand Down Expand Up @@ -863,7 +893,9 @@ def resolvedDependencies(self) -> list[ResourceDescriptor]:
def to_json(self) -> dict:
"""Get the JSON representation of this builder definition."""
return {
self.ATTR_BUILD_TYPE: str(self.buildType),
self.ATTR_BUILD_TYPE: self.buildType.to_json()
if self.buildType
else None,
self.ATTR_EXTERNAL_PARAMETERS: self.externalParameters,
self.ATTR_INTERNAL_PARAMETERS: self.internalParameters,
self.ATTR_RESOLVED_DEPENDENCIES: [
Expand Down Expand Up @@ -1009,6 +1041,15 @@ def is_valid(timestamp: str) -> bool:
return False
return True

def to_json(self) -> str:
"""Get the JSON representation of this timestamp.
The JSON representation of a timestamp is its string representation.
:return: An |str| representing this timestamp value.
""" # noqa RST304
return str(self)


class TypeURI(object):
"""Uniform Resource Identifier as specified in RFC 3986.
Expand Down Expand Up @@ -1068,6 +1109,15 @@ def uri(self) -> str:
"""
return self.__uri

def to_json(self) -> str:
"""Get the JSON representation of this URI.
This simply returns the string representation of this URI.
:return: An |str| representing the JSON value of this URI.
""" # noqa RST304
return self.uri


class ResourceURI(TypeURI):
"""Uniform Resource Identifier as specified in RFC 3986.
Expand Down
Loading

0 comments on commit a0efe5c

Please sign in to comment.