siliconcompiler 0.32.2__py3-none-any.whl → 0.33.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siliconcompiler/__init__.py +19 -2
- siliconcompiler/_metadata.py +3 -2
- siliconcompiler/apps/sc.py +2 -2
- siliconcompiler/apps/sc_install.py +3 -3
- siliconcompiler/apps/sc_issue.py +1 -1
- siliconcompiler/apps/sc_remote.py +4 -4
- siliconcompiler/apps/sc_show.py +2 -2
- siliconcompiler/apps/utils/replay.py +5 -3
- siliconcompiler/asic.py +120 -0
- siliconcompiler/checklist.py +150 -0
- siliconcompiler/core.py +319 -345
- siliconcompiler/{templates → data/templates}/replay/replay.sh.j2 +2 -2
- siliconcompiler/flowgraph.py +803 -515
- siliconcompiler/fpga.py +84 -0
- siliconcompiler/metric.py +420 -0
- siliconcompiler/optimizer/vizier.py +2 -3
- siliconcompiler/package/__init__.py +29 -6
- siliconcompiler/pdk.py +415 -0
- siliconcompiler/record.py +449 -0
- siliconcompiler/remote/client.py +61 -19
- siliconcompiler/remote/schema.py +116 -112
- siliconcompiler/remote/server.py +3 -5
- siliconcompiler/report/__init__.py +3 -2
- siliconcompiler/report/dashboard/__init__.py +61 -170
- siliconcompiler/report/dashboard/cli/__init__.py +79 -0
- siliconcompiler/report/dashboard/cli/board.py +895 -0
- siliconcompiler/report/dashboard/web/__init__.py +196 -0
- siliconcompiler/report/dashboard/{components → web/components}/__init__.py +9 -8
- siliconcompiler/report/dashboard/{components → web/components}/flowgraph.py +3 -3
- siliconcompiler/report/dashboard/{components → web/components}/graph.py +7 -4
- siliconcompiler/report/dashboard/{layouts → web/layouts}/__init__.py +3 -3
- siliconcompiler/report/dashboard/{layouts → web/layouts}/_common.py +1 -1
- siliconcompiler/report/dashboard/{layouts → web/layouts}/vertical_flowgraph.py +5 -5
- siliconcompiler/report/dashboard/{layouts → web/layouts}/vertical_flowgraph_node_tab.py +6 -6
- siliconcompiler/report/dashboard/{layouts → web/layouts}/vertical_flowgraph_sac_tabs.py +6 -6
- siliconcompiler/report/dashboard/{state.py → web/state.py} +1 -1
- siliconcompiler/report/dashboard/{utils → web/utils}/__init__.py +4 -3
- siliconcompiler/report/dashboard/{viewer.py → web/viewer.py} +4 -4
- siliconcompiler/report/html_report.py +2 -3
- siliconcompiler/report/report.py +13 -7
- siliconcompiler/report/summary_image.py +1 -1
- siliconcompiler/report/summary_table.py +3 -3
- siliconcompiler/report/utils.py +11 -10
- siliconcompiler/scheduler/__init__.py +153 -286
- siliconcompiler/scheduler/run_node.py +2 -1
- siliconcompiler/scheduler/send_messages.py +4 -4
- siliconcompiler/scheduler/slurm.py +2 -2
- siliconcompiler/schema/__init__.py +19 -2
- siliconcompiler/schema/baseschema.py +493 -0
- siliconcompiler/schema/cmdlineschema.py +250 -0
- siliconcompiler/{sphinx_ext → schema/docs}/__init__.py +3 -1
- siliconcompiler/{sphinx_ext → schema/docs}/dynamicgen.py +63 -81
- siliconcompiler/{sphinx_ext → schema/docs}/schemagen.py +73 -85
- siliconcompiler/{sphinx_ext → schema/docs}/utils.py +12 -13
- siliconcompiler/schema/editableschema.py +136 -0
- siliconcompiler/schema/journalingschema.py +238 -0
- siliconcompiler/schema/namedschema.py +41 -0
- siliconcompiler/schema/packageschema.py +101 -0
- siliconcompiler/schema/parameter.py +791 -0
- siliconcompiler/schema/parametertype.py +323 -0
- siliconcompiler/schema/parametervalue.py +736 -0
- siliconcompiler/schema/safeschema.py +37 -0
- siliconcompiler/schema/schema_cfg.py +109 -1789
- siliconcompiler/schema/utils.py +5 -68
- siliconcompiler/schema_obj.py +119 -0
- siliconcompiler/tool.py +1308 -0
- siliconcompiler/tools/_common/__init__.py +8 -10
- siliconcompiler/tools/_common/sdc/sc_constraints.sdc +1 -1
- siliconcompiler/tools/bluespec/convert.py +7 -7
- siliconcompiler/tools/builtin/_common.py +1 -1
- siliconcompiler/tools/builtin/concatenate.py +2 -2
- siliconcompiler/tools/builtin/minimum.py +1 -1
- siliconcompiler/tools/builtin/mux.py +2 -1
- siliconcompiler/tools/builtin/nop.py +1 -1
- siliconcompiler/tools/builtin/verify.py +6 -4
- siliconcompiler/tools/chisel/convert.py +4 -4
- siliconcompiler/tools/genfasm/bitstream.py +3 -3
- siliconcompiler/tools/ghdl/convert.py +1 -1
- siliconcompiler/tools/icarus/compile.py +4 -4
- siliconcompiler/tools/icepack/bitstream.py +6 -1
- siliconcompiler/tools/klayout/convert_drc_db.py +5 -0
- siliconcompiler/tools/klayout/klayout_export.py +0 -1
- siliconcompiler/tools/klayout/klayout_utils.py +3 -10
- siliconcompiler/tools/nextpnr/apr.py +6 -1
- siliconcompiler/tools/nextpnr/nextpnr.py +4 -4
- siliconcompiler/tools/openroad/_apr.py +17 -0
- siliconcompiler/tools/openroad/fillmetal_insertion.py +14 -14
- siliconcompiler/tools/openroad/rdlroute.py +3 -3
- siliconcompiler/tools/openroad/scripts/apr/postamble.tcl +1 -1
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +5 -5
- siliconcompiler/tools/openroad/scripts/apr/sc_antenna_repair.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_placement.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_endcap_tapcell_insertion.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_fillercell_insertion.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_fillmetal_insertion.tcl +4 -4
- siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +4 -4
- siliconcompiler/tools/openroad/scripts/apr/sc_metrics.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_pin_placement.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +4 -4
- siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +58 -2
- siliconcompiler/tools/openroad/scripts/common/reports.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -3
- siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +4 -4
- siliconcompiler/tools/openroad/scripts/sc_show.tcl +7 -7
- siliconcompiler/tools/opensta/__init__.py +1 -1
- siliconcompiler/tools/opensta/scripts/sc_check_library.tcl +1 -1
- siliconcompiler/tools/opensta/scripts/sc_procs.tcl +16 -0
- siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +1 -1
- siliconcompiler/tools/opensta/scripts/sc_timing.tcl +35 -7
- siliconcompiler/tools/opensta/timing.py +6 -2
- siliconcompiler/tools/slang/__init__.py +10 -10
- siliconcompiler/tools/surelog/parse.py +4 -4
- siliconcompiler/tools/sv2v/convert.py +20 -3
- siliconcompiler/tools/verilator/compile.py +2 -2
- siliconcompiler/tools/verilator/verilator.py +3 -3
- siliconcompiler/tools/vpr/place.py +1 -1
- siliconcompiler/tools/vpr/route.py +4 -4
- siliconcompiler/tools/vpr/screenshot.py +1 -1
- siliconcompiler/tools/vpr/show.py +5 -5
- siliconcompiler/tools/vpr/vpr.py +24 -24
- siliconcompiler/tools/xdm/convert.py +2 -2
- siliconcompiler/tools/xyce/simulate.py +1 -1
- siliconcompiler/tools/yosys/sc_synth_asic.tcl +104 -90
- siliconcompiler/tools/yosys/syn_asic.py +13 -4
- siliconcompiler/toolscripts/_tools.json +12 -7
- siliconcompiler/toolscripts/rhel8/install-chisel.sh +2 -0
- siliconcompiler/toolscripts/rhel8/install-icarus.sh +1 -0
- siliconcompiler/toolscripts/rhel8/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/rhel8/install-magic.sh +1 -2
- siliconcompiler/toolscripts/rhel8/install-netgen.sh +1 -1
- siliconcompiler/toolscripts/rhel8/install-slang.sh +2 -0
- siliconcompiler/toolscripts/rhel8/install-surelog.sh +3 -1
- siliconcompiler/toolscripts/rhel8/install-sv2v.sh +1 -0
- siliconcompiler/toolscripts/rhel8/install-verible.sh +2 -0
- siliconcompiler/toolscripts/rhel8/install-verilator.sh +1 -0
- siliconcompiler/toolscripts/rhel8/install-xyce.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-chisel.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-ghdl.sh +1 -0
- siliconcompiler/toolscripts/rhel9/install-gtkwave.sh +1 -0
- siliconcompiler/toolscripts/rhel9/install-icarus.sh +1 -0
- siliconcompiler/toolscripts/rhel9/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-magic.sh +1 -2
- siliconcompiler/toolscripts/rhel9/install-netgen.sh +1 -1
- siliconcompiler/toolscripts/rhel9/install-openroad.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-opensta.sh +76 -0
- siliconcompiler/toolscripts/rhel9/install-slang.sh +3 -1
- siliconcompiler/toolscripts/rhel9/install-surelog.sh +2 -1
- siliconcompiler/toolscripts/rhel9/install-sv2v.sh +1 -0
- siliconcompiler/toolscripts/rhel9/install-verible.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-verilator.sh +1 -0
- siliconcompiler/toolscripts/rhel9/install-vpr.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-xdm.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-xyce.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +2 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +3 -1
- siliconcompiler/toolscripts/rhel9/install-yosys.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-bambu.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-bluespec.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-chisel.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-ghdl.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-gtkwave.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-icarus.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-icepack.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-magic.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-netgen.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-nextpnr.sh +1 -3
- siliconcompiler/toolscripts/ubuntu20/install-openroad.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-opensta.sh +72 -0
- siliconcompiler/toolscripts/ubuntu20/install-slang.sh +3 -1
- siliconcompiler/toolscripts/ubuntu20/install-slurm.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-surelog.sh +3 -1
- siliconcompiler/toolscripts/ubuntu20/install-sv2v.sh +1 -1
- siliconcompiler/toolscripts/ubuntu20/install-verible.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-verilator.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-xdm.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-xyce.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +2 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-bambu.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-bluespec.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-chisel.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-ghdl.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-gtkwave.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-icarus.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-icepack.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-magic.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-netgen.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-nextpnr.sh +1 -2
- siliconcompiler/toolscripts/ubuntu22/install-openroad.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-opensta.sh +72 -0
- siliconcompiler/toolscripts/ubuntu22/install-slang.sh +3 -1
- siliconcompiler/toolscripts/ubuntu22/install-slurm.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-surelog.sh +3 -1
- siliconcompiler/toolscripts/ubuntu22/install-sv2v.sh +1 -1
- siliconcompiler/toolscripts/ubuntu22/install-verible.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-verilator.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-vpr.sh +2 -2
- siliconcompiler/toolscripts/ubuntu22/install-xdm.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-xyce.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +2 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +3 -1
- siliconcompiler/toolscripts/ubuntu22/install-yosys.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-bambu.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-bluespec.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-chisel.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-ghdl.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-gtkwave.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-icarus.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-icepack.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-magic.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-netgen.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-nextpnr.sh +1 -3
- siliconcompiler/toolscripts/ubuntu24/install-openroad.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-opensta.sh +72 -0
- siliconcompiler/toolscripts/ubuntu24/install-slang.sh +3 -1
- siliconcompiler/toolscripts/ubuntu24/install-slurm.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-surelog.sh +3 -1
- siliconcompiler/toolscripts/ubuntu24/install-sv2v.sh +1 -1
- siliconcompiler/toolscripts/ubuntu24/install-verible.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-verilator.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-vpr.sh +2 -2
- siliconcompiler/toolscripts/ubuntu24/install-xdm.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-xyce.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +3 -1
- siliconcompiler/toolscripts/ubuntu24/install-yosys.sh +2 -0
- siliconcompiler/utils/__init__.py +8 -112
- siliconcompiler/utils/flowgraph.py +339 -0
- siliconcompiler/{issue.py → utils/issue.py} +7 -4
- siliconcompiler/utils/logging.py +86 -33
- {siliconcompiler-0.32.2.dist-info → siliconcompiler-0.33.0.dist-info}/METADATA +10 -8
- siliconcompiler-0.33.0.dist-info/RECORD +487 -0
- {siliconcompiler-0.32.2.dist-info → siliconcompiler-0.33.0.dist-info}/WHEEL +1 -1
- {siliconcompiler-0.32.2.dist-info → siliconcompiler-0.33.0.dist-info}/entry_points.txt +8 -8
- siliconcompiler/schema/schema_obj.py +0 -1936
- siliconcompiler/toolscripts/ubuntu20/install-vpr.sh +0 -27
- siliconcompiler/toolscripts/ubuntu20/install-yosys-parmys.sh +0 -59
- siliconcompiler-0.32.2.dist-info/RECORD +0 -464
- /siliconcompiler/{templates → data/templates}/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/email/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/email/general.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/email/summary.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/issue/README.txt +0 -0
- /siliconcompiler/{templates → data/templates}/issue/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/issue/run.sh +0 -0
- /siliconcompiler/{templates → data/templates}/replay/replay.py.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/replay/requirements.txt +0 -0
- /siliconcompiler/{templates → data/templates}/replay/setup.sh +0 -0
- /siliconcompiler/{templates → data/templates}/report/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap.min.css +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap.min.js +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap_LICENSE.md +0 -0
- /siliconcompiler/{templates → data/templates}/report/sc_report.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/slurm/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/slurm/run.sh +0 -0
- /siliconcompiler/{templates → data/templates}/tcl/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/tcl/manifest.tcl.j2 +0 -0
- /siliconcompiler/report/dashboard/{utils → web/utils}/file_utils.py +0 -0
- /siliconcompiler/{units.py → utils/units.py} +0 -0
- {siliconcompiler-0.32.2.dist-info → siliconcompiler-0.33.0.dist-info}/licenses/LICENSE +0 -0
- {siliconcompiler-0.32.2.dist-info → siliconcompiler-0.33.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import hashlib
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
|
|
6
|
+
import os.path
|
|
7
|
+
|
|
8
|
+
from .parametertype import NodeType
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from base64 import b64encode, b64decode
|
|
12
|
+
from hashlib import blake2b
|
|
13
|
+
_has_sign = True
|
|
14
|
+
except: # noqa E722
|
|
15
|
+
_has_sign = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NodeListValue:
|
|
19
|
+
'''
|
|
20
|
+
Holds the data for a list schema type.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
base (:class:`NodeValue`): base type for this list.
|
|
24
|
+
'''
|
|
25
|
+
|
|
26
|
+
def __init__(self, base):
|
|
27
|
+
self.__base = base
|
|
28
|
+
self.__values = []
|
|
29
|
+
|
|
30
|
+
def getdict(self):
|
|
31
|
+
"""
|
|
32
|
+
Returns a schema dictionary.
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
>>> value.getdict()
|
|
36
|
+
Returns the complete dictionary for the value
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
manifest = {}
|
|
40
|
+
for field in self.fields:
|
|
41
|
+
if field is None:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
value = self.get(field=field)
|
|
45
|
+
manifest.setdefault(field, []).extend(value)
|
|
46
|
+
|
|
47
|
+
if field == "author":
|
|
48
|
+
tmplist = []
|
|
49
|
+
for a in manifest[field]:
|
|
50
|
+
tmplist.extend(a)
|
|
51
|
+
manifest[field] = tmplist
|
|
52
|
+
return manifest
|
|
53
|
+
|
|
54
|
+
def _from_dict(self, manifest, keypath, version):
|
|
55
|
+
'''
|
|
56
|
+
Create a new value based on the provided dictionary.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
manifest (dict): Manifest to decide.
|
|
60
|
+
keypath (list of str): Path to the current keypath.
|
|
61
|
+
version (packaging.Version): Version of the dictionary schema
|
|
62
|
+
sctype (str): schema type for this value
|
|
63
|
+
'''
|
|
64
|
+
|
|
65
|
+
self.__values.clear()
|
|
66
|
+
for n in range(len(manifest["value"])):
|
|
67
|
+
param = self.__base.copy()
|
|
68
|
+
self.__values.append(param)
|
|
69
|
+
|
|
70
|
+
for field in self.fields:
|
|
71
|
+
if field is None:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
if len(manifest[field]) <= n:
|
|
75
|
+
continue
|
|
76
|
+
param.set(manifest[field][n], field=field)
|
|
77
|
+
|
|
78
|
+
def get(self, field='value'):
|
|
79
|
+
"""
|
|
80
|
+
Returns the value in the specified field
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
field (str): name of schema field.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
vals = []
|
|
87
|
+
for val in self.__values:
|
|
88
|
+
value = val.get(field=field)
|
|
89
|
+
vals.append(value)
|
|
90
|
+
return vals
|
|
91
|
+
|
|
92
|
+
def set(self, value, field='value'):
|
|
93
|
+
"""
|
|
94
|
+
Sets the value in a specific field and ensures it has been normalized.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
tuple of modified values
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
value (any): value to set
|
|
101
|
+
field (str): field to set
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
value = NodeType.normalize(value, self.type)
|
|
105
|
+
|
|
106
|
+
if field == 'value':
|
|
107
|
+
self.__values.clear()
|
|
108
|
+
else:
|
|
109
|
+
if len(value) != len(self.__values):
|
|
110
|
+
raise ValueError(f"set on {field} field must match number of values")
|
|
111
|
+
|
|
112
|
+
modified = list()
|
|
113
|
+
for n in range(len(value)):
|
|
114
|
+
if field == 'value':
|
|
115
|
+
self.__values.append(self.__base.copy())
|
|
116
|
+
self.__values[n].set(value[n], field=field)
|
|
117
|
+
modified.append(self.__values[n])
|
|
118
|
+
return tuple(modified)
|
|
119
|
+
|
|
120
|
+
def add(self, value, field='value'):
|
|
121
|
+
"""
|
|
122
|
+
Adds the value in a specific field and ensures it has been normalized.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
tuple of modified values
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
value (any): value to set
|
|
129
|
+
field (str): field to set
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
modified = list()
|
|
133
|
+
if field == 'value':
|
|
134
|
+
value = NodeType.normalize(value, self.type)
|
|
135
|
+
|
|
136
|
+
for n in range(len(value)):
|
|
137
|
+
self.__values.append(self.__base.copy())
|
|
138
|
+
self.__values[-1].set(value[n], field=field)
|
|
139
|
+
modified.append(self.__values[-1])
|
|
140
|
+
else:
|
|
141
|
+
for val in self.__values:
|
|
142
|
+
val.add(value, field=field)
|
|
143
|
+
modified.append(val)
|
|
144
|
+
return tuple(modified)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def fields(self):
|
|
148
|
+
"""
|
|
149
|
+
Returns a list of valid fields for this value
|
|
150
|
+
"""
|
|
151
|
+
return self.__base.fields
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def values(self):
|
|
155
|
+
'''
|
|
156
|
+
Returns a copy of the values stored in the list
|
|
157
|
+
'''
|
|
158
|
+
return self.__values.copy()
|
|
159
|
+
|
|
160
|
+
def copy(self):
|
|
161
|
+
"""
|
|
162
|
+
Returns a copy of this value.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
return copy.deepcopy(self)
|
|
166
|
+
|
|
167
|
+
def _set_type(self, sctype):
|
|
168
|
+
sctype = NodeType.parse(sctype)[0]
|
|
169
|
+
self.__base._set_type(sctype)
|
|
170
|
+
for val in self.__values:
|
|
171
|
+
val._set_type(sctype)
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def type(self):
|
|
175
|
+
"""
|
|
176
|
+
Returns the type for this value
|
|
177
|
+
"""
|
|
178
|
+
return [self.__base.type]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class NodeValue:
|
|
182
|
+
'''
|
|
183
|
+
Holds the data for a parameter.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
sctype (str): type for this value
|
|
187
|
+
value (any): default value for this parameter
|
|
188
|
+
'''
|
|
189
|
+
|
|
190
|
+
def __init__(self, sctype, value=None):
|
|
191
|
+
self._set_type(sctype)
|
|
192
|
+
self.__value = value
|
|
193
|
+
self.__signature = None
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def from_dict(cls, manifest, keypath, version, sctype):
|
|
197
|
+
'''
|
|
198
|
+
Create a new value based on the provided dictionary.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
manifest (dict): Manifest to decide.
|
|
202
|
+
keypath (list of str): Path to the current keypath.
|
|
203
|
+
version (packaging.Version): Version of the dictionary schema
|
|
204
|
+
sctype (str): schema type for this value
|
|
205
|
+
'''
|
|
206
|
+
|
|
207
|
+
# create a dummy value
|
|
208
|
+
nodeval = cls(sctype)
|
|
209
|
+
nodeval._from_dict(manifest, keypath, version)
|
|
210
|
+
return nodeval
|
|
211
|
+
|
|
212
|
+
def getdict(self):
|
|
213
|
+
"""
|
|
214
|
+
Returns a schema dictionary.
|
|
215
|
+
|
|
216
|
+
Examples:
|
|
217
|
+
>>> value.getdict()
|
|
218
|
+
Returns the complete dictionary for the value
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"value": self.get(field="value"),
|
|
223
|
+
"signature": self.get(field="signature")
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
def _from_dict(self, manifest, keypath, version):
|
|
227
|
+
'''
|
|
228
|
+
Copies the information from the manifest into this value.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
manifest (dict): Manifest to decide.
|
|
232
|
+
keypath (list of str): Path to the current keypath.
|
|
233
|
+
version (packaging.Version): Version of the dictionary schema
|
|
234
|
+
'''
|
|
235
|
+
|
|
236
|
+
self.set(manifest["value"], field="value")
|
|
237
|
+
self.set(manifest["signature"], field="signature")
|
|
238
|
+
|
|
239
|
+
def get(self, field='value'):
|
|
240
|
+
"""
|
|
241
|
+
Returns the value in the specified field
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
field (str): name of schema field.
|
|
245
|
+
"""
|
|
246
|
+
if field is None:
|
|
247
|
+
return self
|
|
248
|
+
if field == 'value':
|
|
249
|
+
return copy.deepcopy(self.__value)
|
|
250
|
+
if field == 'signature':
|
|
251
|
+
return self.__signature
|
|
252
|
+
raise ValueError(f"{field} is not a valid field")
|
|
253
|
+
|
|
254
|
+
def set(self, value, field='value'):
|
|
255
|
+
"""
|
|
256
|
+
Sets the value in a specific field and ensures it has been normalized.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
self
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
value (any): value to set
|
|
263
|
+
field (str): field to set
|
|
264
|
+
"""
|
|
265
|
+
if field == 'value':
|
|
266
|
+
self.__value = NodeType.normalize(value, self.type)
|
|
267
|
+
return self
|
|
268
|
+
if field == 'signature':
|
|
269
|
+
self.__signature = NodeType.normalize(value, "str")
|
|
270
|
+
return self
|
|
271
|
+
raise ValueError(f"{field} is not a valid field")
|
|
272
|
+
|
|
273
|
+
def add(self, value, field='value'):
|
|
274
|
+
"""
|
|
275
|
+
Not valid for this datatype, will raise a ValueError
|
|
276
|
+
"""
|
|
277
|
+
raise ValueError(f"cannot add to {field} field")
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def fields(self):
|
|
281
|
+
"""
|
|
282
|
+
Returns a list of valid fields for this value
|
|
283
|
+
"""
|
|
284
|
+
return (None, "value", "signature")
|
|
285
|
+
|
|
286
|
+
def copy(self):
|
|
287
|
+
"""
|
|
288
|
+
Returns a copy of this value.
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
return copy.deepcopy(self)
|
|
292
|
+
|
|
293
|
+
def _set_type(self, sctype):
|
|
294
|
+
self.__type = NodeType.parse(sctype)
|
|
295
|
+
|
|
296
|
+
def __compute_signature(self, person, key, salt):
|
|
297
|
+
h = blake2b(key=key, salt=salt, person=person)
|
|
298
|
+
for field in self.fields:
|
|
299
|
+
if field is None:
|
|
300
|
+
continue
|
|
301
|
+
if field == 'signature':
|
|
302
|
+
# dont include signature field in hash
|
|
303
|
+
continue
|
|
304
|
+
h.update(str(self.get(field=field)).encode("utf-8"))
|
|
305
|
+
return h.hexdigest()
|
|
306
|
+
|
|
307
|
+
def sign(self, person, key, salt=None):
|
|
308
|
+
"""
|
|
309
|
+
Generate a signature for this value.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
person (str): Identification for this person signing this value
|
|
313
|
+
key (str): Key to used to sign this value
|
|
314
|
+
salt (bytes): salt to use, if not specified, a random number will be selected.
|
|
315
|
+
"""
|
|
316
|
+
if not _has_sign:
|
|
317
|
+
raise RuntimeError("encoding not available")
|
|
318
|
+
|
|
319
|
+
person = person.encode("utf-8")
|
|
320
|
+
key = key.encode("utf-8")
|
|
321
|
+
if not salt:
|
|
322
|
+
salt = os.urandom(blake2b.SALT_SIZE)
|
|
323
|
+
else:
|
|
324
|
+
salt = salt.encode("utf-8")
|
|
325
|
+
|
|
326
|
+
digest = self.__compute_signature(person=person, key=key, salt=salt)
|
|
327
|
+
encode_person = b64encode(person).decode("utf-8")
|
|
328
|
+
encode_salt = b64encode(salt).decode("utf-8")
|
|
329
|
+
self.__signature = f"{encode_person}:{encode_salt}:{digest}"
|
|
330
|
+
|
|
331
|
+
def verify_signature(self, person, key):
|
|
332
|
+
"""
|
|
333
|
+
Verify the signature of this value.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
person (str): Identification for this person signing this value
|
|
337
|
+
key (str): Key to used to sign this value
|
|
338
|
+
"""
|
|
339
|
+
if not self.__signature:
|
|
340
|
+
raise ValueError("no signature available")
|
|
341
|
+
|
|
342
|
+
if not _has_sign:
|
|
343
|
+
raise RuntimeError("encoding not available")
|
|
344
|
+
|
|
345
|
+
key = key.encode("utf-8")
|
|
346
|
+
person = person.encode("utf-8")
|
|
347
|
+
encode_person, encode_salt, digest = self.__signature.split(":")
|
|
348
|
+
check_person = b64encode(person).decode("utf-8")
|
|
349
|
+
|
|
350
|
+
if check_person != encode_person:
|
|
351
|
+
raise ValueError(f"{person.decode('utf-8')} does not match signing "
|
|
352
|
+
f"person: {b64decode(encode_person).decode('utf-8')}")
|
|
353
|
+
|
|
354
|
+
decode_salt = b64decode(encode_salt)
|
|
355
|
+
check_digest = self.__compute_signature(person=person, key=key, salt=decode_salt)
|
|
356
|
+
|
|
357
|
+
if check_digest == digest:
|
|
358
|
+
return True
|
|
359
|
+
return False
|
|
360
|
+
|
|
361
|
+
@property
|
|
362
|
+
def type(self):
|
|
363
|
+
"""
|
|
364
|
+
Returns the type for this value
|
|
365
|
+
"""
|
|
366
|
+
return self.__type
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class PathNodeValue(NodeValue):
|
|
370
|
+
'''
|
|
371
|
+
Holds the path data for a parameter.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
type (str): type of path
|
|
375
|
+
value (any): default value for this parameter
|
|
376
|
+
'''
|
|
377
|
+
|
|
378
|
+
def __init__(self, type, value=None):
|
|
379
|
+
super().__init__(type, value=value)
|
|
380
|
+
self.__filehash = None
|
|
381
|
+
self.__package = None
|
|
382
|
+
|
|
383
|
+
def getdict(self):
|
|
384
|
+
return {
|
|
385
|
+
**super().getdict(),
|
|
386
|
+
"filehash": self.get(field="filehash"),
|
|
387
|
+
"package": self.get(field="package")
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
def _from_dict(self, manifest, keypath, version):
|
|
391
|
+
super()._from_dict(manifest, keypath, version)
|
|
392
|
+
|
|
393
|
+
self.set(manifest["filehash"], field="filehash")
|
|
394
|
+
self.set(manifest["package"], field="package")
|
|
395
|
+
|
|
396
|
+
def get(self, field='value'):
|
|
397
|
+
if field == 'filehash':
|
|
398
|
+
return self.__filehash
|
|
399
|
+
if field == 'package':
|
|
400
|
+
return self.__package
|
|
401
|
+
return super().get(field=field)
|
|
402
|
+
|
|
403
|
+
def set(self, value, field='value'):
|
|
404
|
+
if field == 'filehash':
|
|
405
|
+
self.__filehash = NodeType.normalize(value, "str")
|
|
406
|
+
return self
|
|
407
|
+
if field == 'package':
|
|
408
|
+
self.__package = NodeType.normalize(value, "str")
|
|
409
|
+
return self
|
|
410
|
+
return super().set(value, field=field)
|
|
411
|
+
|
|
412
|
+
def __resolve_collection_path(self, path, collection_dir):
|
|
413
|
+
try:
|
|
414
|
+
collected_paths = os.listdir(collection_dir)
|
|
415
|
+
if not collected_paths:
|
|
416
|
+
return None
|
|
417
|
+
except FileNotFoundError:
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
path_paths = pathlib.PurePosixPath(path).parts
|
|
421
|
+
for n in range(len(path_paths)):
|
|
422
|
+
# Search through the path elements to see if any of the previous path parts
|
|
423
|
+
# have been imported
|
|
424
|
+
|
|
425
|
+
n += 1
|
|
426
|
+
basename = str(pathlib.PurePosixPath(*path_paths[0:n]))
|
|
427
|
+
endname = str(pathlib.PurePosixPath(*path_paths[n:]))
|
|
428
|
+
|
|
429
|
+
import_name = PathNodeValue.generate_hashed_path(basename, self.__package)
|
|
430
|
+
if import_name not in collected_paths:
|
|
431
|
+
continue
|
|
432
|
+
|
|
433
|
+
abspath = os.path.join(collection_dir, import_name)
|
|
434
|
+
if endname:
|
|
435
|
+
abspath = os.path.join(abspath, endname)
|
|
436
|
+
abspath = os.path.abspath(abspath)
|
|
437
|
+
if os.path.exists(abspath):
|
|
438
|
+
return abspath
|
|
439
|
+
|
|
440
|
+
return None
|
|
441
|
+
|
|
442
|
+
@staticmethod
|
|
443
|
+
def resolve_env_vars(path, envvars=None):
|
|
444
|
+
"""
|
|
445
|
+
Resolve environment variables in a path.
|
|
446
|
+
|
|
447
|
+
Returns the expended path.
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
path (str): path to expand
|
|
451
|
+
envvars (dict): environmental variables to use during resolution.
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
if not path:
|
|
455
|
+
return path
|
|
456
|
+
|
|
457
|
+
# Handle environmental expansions
|
|
458
|
+
if not envvars:
|
|
459
|
+
envvars = {}
|
|
460
|
+
|
|
461
|
+
# Resolve env vars and user home
|
|
462
|
+
env_save = os.environ.copy()
|
|
463
|
+
os.environ.update(envvars)
|
|
464
|
+
path = os.path.expandvars(path)
|
|
465
|
+
path = os.path.expanduser(path)
|
|
466
|
+
os.environ.clear()
|
|
467
|
+
os.environ.update(env_save)
|
|
468
|
+
|
|
469
|
+
return path
|
|
470
|
+
|
|
471
|
+
def resolve_path(self, envvars=None, search=None, collection_dir=None):
|
|
472
|
+
"""
|
|
473
|
+
Resolve the path of this value.
|
|
474
|
+
|
|
475
|
+
Returns the absolute path if found, otherwise raises a FileNotFoundError.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
envvars (dict): environmental variables to use during resolution.
|
|
479
|
+
search (list of paths): list of paths to search to check for the path.
|
|
480
|
+
collection_dir (path): path to collection directory.
|
|
481
|
+
"""
|
|
482
|
+
value = self.get()
|
|
483
|
+
if value is None:
|
|
484
|
+
return None
|
|
485
|
+
|
|
486
|
+
# Handle environmental expansions
|
|
487
|
+
value = PathNodeValue.resolve_env_vars(value, envvars=envvars)
|
|
488
|
+
|
|
489
|
+
# Check collections path
|
|
490
|
+
if collection_dir:
|
|
491
|
+
collect_path = self.__resolve_collection_path(value, collection_dir)
|
|
492
|
+
if collect_path:
|
|
493
|
+
return collect_path
|
|
494
|
+
|
|
495
|
+
if os.path.isabs(value) and os.path.exists(value):
|
|
496
|
+
return value
|
|
497
|
+
|
|
498
|
+
# Search for file
|
|
499
|
+
if search is None:
|
|
500
|
+
search = [os.getcwd()]
|
|
501
|
+
|
|
502
|
+
for searchdir in search:
|
|
503
|
+
abspath = os.path.abspath(os.path.join(searchdir, value))
|
|
504
|
+
if os.path.exists(abspath):
|
|
505
|
+
return abspath
|
|
506
|
+
|
|
507
|
+
# File not found
|
|
508
|
+
raise FileNotFoundError(value)
|
|
509
|
+
|
|
510
|
+
@staticmethod
|
|
511
|
+
def generate_hashed_path(path, package):
|
|
512
|
+
'''
|
|
513
|
+
Utility to map file to an unambiguous name based on its path.
|
|
514
|
+
|
|
515
|
+
The mapping looks like:
|
|
516
|
+
path/to/file.ext => file_<hash('path/to')>.ext
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
path (str): path to directory or file
|
|
520
|
+
package (str): name of package this file belongs to
|
|
521
|
+
'''
|
|
522
|
+
path = pathlib.PurePosixPath(path)
|
|
523
|
+
ext = ''.join(path.suffixes)
|
|
524
|
+
|
|
525
|
+
# strip off all file suffixes to get just the bare name
|
|
526
|
+
barepath = path
|
|
527
|
+
while barepath.suffix:
|
|
528
|
+
barepath = pathlib.PurePosixPath(barepath.stem)
|
|
529
|
+
filename = str(barepath.parts[-1])
|
|
530
|
+
|
|
531
|
+
if not package:
|
|
532
|
+
package = ''
|
|
533
|
+
else:
|
|
534
|
+
package = f'{package}:'
|
|
535
|
+
|
|
536
|
+
path_to_hash = f'{package}{str(path.parent)}'
|
|
537
|
+
|
|
538
|
+
pathhash = hashlib.sha1(path_to_hash.encode('utf-8')).hexdigest()
|
|
539
|
+
|
|
540
|
+
return f'{filename}_{pathhash}{ext}'
|
|
541
|
+
|
|
542
|
+
def get_hashed_filename(self):
|
|
543
|
+
'''
|
|
544
|
+
Utility to map file to an unambiguous name based on its path.
|
|
545
|
+
|
|
546
|
+
The mapping looks like:
|
|
547
|
+
path/to/file.ext => file_<hash('path/to')>.ext
|
|
548
|
+
'''
|
|
549
|
+
return PathNodeValue.generate_hashed_path(self.get(), self.__package)
|
|
550
|
+
|
|
551
|
+
def hash(self, function, **kwargs):
|
|
552
|
+
"""
|
|
553
|
+
Compute the hash for this path.
|
|
554
|
+
|
|
555
|
+
Keyword arguments are derived from :meth:`resolve_path`.
|
|
556
|
+
|
|
557
|
+
Args:
|
|
558
|
+
function (str): name of hashing function to use.
|
|
559
|
+
"""
|
|
560
|
+
raise NotImplementedError
|
|
561
|
+
|
|
562
|
+
@staticmethod
|
|
563
|
+
def hash_directory(dirname, hashobj=None, hashfunction=None):
|
|
564
|
+
"""
|
|
565
|
+
Compute the hash for this directory.
|
|
566
|
+
|
|
567
|
+
Args:
|
|
568
|
+
dirname (path): directory to hash
|
|
569
|
+
hashobj (hashlib.): hashing object
|
|
570
|
+
hashfunction (str): name of hashing function to use
|
|
571
|
+
"""
|
|
572
|
+
|
|
573
|
+
if dirname is None:
|
|
574
|
+
return None
|
|
575
|
+
|
|
576
|
+
if not hashobj:
|
|
577
|
+
hashfunc = getattr(hashlib, hashfunction, None)
|
|
578
|
+
if not hashfunc:
|
|
579
|
+
raise RuntimeError("Unable to hash directory due to missing "
|
|
580
|
+
f"hash function: {hashfunction}")
|
|
581
|
+
hashobj = hashfunc()
|
|
582
|
+
|
|
583
|
+
all_files = []
|
|
584
|
+
for root, _, files in os.walk(dirname):
|
|
585
|
+
all_files.extend([os.path.join(root, f) for f in files])
|
|
586
|
+
dirhash = None
|
|
587
|
+
hashobj = hashfunc()
|
|
588
|
+
for file in sorted(all_files):
|
|
589
|
+
# Cast everything to a windows path and convert to posix.
|
|
590
|
+
# https://stackoverflow.com/questions/73682260
|
|
591
|
+
posix_path = pathlib.PureWindowsPath(os.path.relpath(file, dirname)).as_posix()
|
|
592
|
+
hashobj.update(posix_path.encode("utf-8"))
|
|
593
|
+
dirhash = PathNodeValue.hash_file(file, hashobj=hashobj)
|
|
594
|
+
return dirhash
|
|
595
|
+
|
|
596
|
+
@staticmethod
|
|
597
|
+
def hash_file(filename, hashobj=None, hashfunction=None):
|
|
598
|
+
"""
|
|
599
|
+
Compute the hash for this file.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
filename (path): file to hash
|
|
603
|
+
hashobj (hashlib.): hashing object
|
|
604
|
+
hashfunction (str): name of hashing function to use
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
if filename is None:
|
|
608
|
+
return None
|
|
609
|
+
|
|
610
|
+
if not hashobj:
|
|
611
|
+
hashfunc = getattr(hashlib, hashfunction, None)
|
|
612
|
+
if not hashfunc:
|
|
613
|
+
raise RuntimeError("Unable to hash file due to missing "
|
|
614
|
+
f"hash function: {hashfunction}")
|
|
615
|
+
hashobj = hashfunc()
|
|
616
|
+
|
|
617
|
+
with open(filename, "rb") as f:
|
|
618
|
+
for byte_block in iter(lambda: f.read(4096), b""):
|
|
619
|
+
hashobj.update(byte_block)
|
|
620
|
+
return hashobj.hexdigest()
|
|
621
|
+
|
|
622
|
+
@property
|
|
623
|
+
def fields(self):
|
|
624
|
+
return (*super().fields, "filehash", "package")
|
|
625
|
+
|
|
626
|
+
@property
|
|
627
|
+
def type(self):
|
|
628
|
+
raise NotImplementedError
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
class DirectoryNodeValue(PathNodeValue):
|
|
632
|
+
'''
|
|
633
|
+
Holds the directory data for a parameter.
|
|
634
|
+
|
|
635
|
+
Args:
|
|
636
|
+
value (any): default value for this parameter
|
|
637
|
+
'''
|
|
638
|
+
|
|
639
|
+
def __init__(self, value=None):
|
|
640
|
+
super().__init__("dir", value=value)
|
|
641
|
+
|
|
642
|
+
def hash(self, function, **kwargs):
|
|
643
|
+
"""
|
|
644
|
+
Compute the hash for this directory.
|
|
645
|
+
|
|
646
|
+
Keyword arguments are derived from :meth:`resolve_path`.
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
function (str): name of hashing function to use.
|
|
650
|
+
"""
|
|
651
|
+
return PathNodeValue.hash_directory(
|
|
652
|
+
self.resolve_path(**kwargs), hashfunction=function)
|
|
653
|
+
|
|
654
|
+
@property
|
|
655
|
+
def type(self):
|
|
656
|
+
return "dir"
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
class FileNodeValue(PathNodeValue):
|
|
660
|
+
'''
|
|
661
|
+
Holds the file data for a parameter.
|
|
662
|
+
|
|
663
|
+
Args:
|
|
664
|
+
value (any): default value for this parameter
|
|
665
|
+
'''
|
|
666
|
+
|
|
667
|
+
def __init__(self, value=None):
|
|
668
|
+
super().__init__("file", value=value)
|
|
669
|
+
self.__date = None
|
|
670
|
+
self.__author = []
|
|
671
|
+
|
|
672
|
+
def getdict(self):
|
|
673
|
+
return {
|
|
674
|
+
**super().getdict(),
|
|
675
|
+
"date": self.get(field="date"),
|
|
676
|
+
"author": self.get(field="author")
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
def _from_dict(self, manifest, keypath, version):
|
|
680
|
+
super()._from_dict(manifest, keypath, version)
|
|
681
|
+
|
|
682
|
+
self.set(manifest["date"], field="date")
|
|
683
|
+
self.set(manifest["author"], field="author")
|
|
684
|
+
|
|
685
|
+
def get(self, field='value'):
|
|
686
|
+
if field == 'date':
|
|
687
|
+
return self.__date
|
|
688
|
+
if field == 'author':
|
|
689
|
+
return self.__author.copy()
|
|
690
|
+
return super().get(field=field)
|
|
691
|
+
|
|
692
|
+
def set(self, value, field='value'):
|
|
693
|
+
if field == 'date':
|
|
694
|
+
self.__date = NodeType.normalize(value, "str")
|
|
695
|
+
return self
|
|
696
|
+
if field == 'author':
|
|
697
|
+
self.__author = NodeType.normalize(value, ["str"])
|
|
698
|
+
return self
|
|
699
|
+
return super().set(value, field=field)
|
|
700
|
+
|
|
701
|
+
def add(self, value, field='value'):
|
|
702
|
+
"""
|
|
703
|
+
Adds the value in a specific field and ensures it has been normalized.
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
self
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
value (any): value to set
|
|
710
|
+
field (str): field to set
|
|
711
|
+
"""
|
|
712
|
+
|
|
713
|
+
if field == 'author':
|
|
714
|
+
self.__author.extend(NodeType.normalize(value, ["str"]))
|
|
715
|
+
return self
|
|
716
|
+
return super().add(value, field=field)
|
|
717
|
+
|
|
718
|
+
def hash(self, function, **kwargs):
|
|
719
|
+
"""
|
|
720
|
+
Compute the hash for this file.
|
|
721
|
+
|
|
722
|
+
Keyword arguments are derived from :meth:`resolve_path`.
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
function (str): name of hashing function to use.
|
|
726
|
+
"""
|
|
727
|
+
return PathNodeValue.hash_file(
|
|
728
|
+
self.resolve_path(**kwargs), hashfunction=function)
|
|
729
|
+
|
|
730
|
+
@property
|
|
731
|
+
def fields(self):
|
|
732
|
+
return (*super().fields, "date", "author")
|
|
733
|
+
|
|
734
|
+
@property
|
|
735
|
+
def type(self):
|
|
736
|
+
return "file"
|