Skip to content

Commit 5a96a32

Browse files
committed
Doc update and fix for json writing
1 parent 8150e46 commit 5a96a32

File tree

1 file changed

+100
-27
lines changed

1 file changed

+100
-27
lines changed

src/sasctl/pzmm/write_json_files.py

Lines changed: 100 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,50 +1004,105 @@ def createRequirementsJSON(cls, jPath=Path.cwd()):
10041004
packageList = picklePackages + codeDependencies
10051005
packageAndVersion = cls.getLocalPackageVersion(list(set(packageList)))
10061006

1007-
with open(Path(jPath) / "requirements.json") as file:
1008-
for package, version in packageAndVersion:
1007+
# Identify packages with missing versions
1008+
missingPackageVersions = [item[0] for item in packageAndVersion if not item[1]]
1009+
1010+
with open(Path(jPath) / "requirements.json", "w") as file:
1011+
if missingPackageVersions:
10091012
jsonStep = json.dumps(
1010-
[
1011-
{
1012-
"step": "install " + package,
1013-
"command": "pip install " + package + "==" + version,
1014-
}
1015-
],
1016-
indent=4,
1017-
)
1013+
[
1014+
{
1015+
"Warning": "The versions for the following packages could not be determined",
1016+
"Packages": ", ".join(missingPackageVersions)
1017+
}
1018+
],
1019+
indent=4,
1020+
)
1021+
file.write(jsonStep)
1022+
for package, version in packageAndVersion:
1023+
if version:
1024+
jsonStep = json.dumps(
1025+
[
1026+
{
1027+
"step": "install " + package,
1028+
"command": "pip install " + package + "==" + version,
1029+
}
1030+
],
1031+
indent=4,
1032+
)
1033+
else:
1034+
jsonStep = json.dumps(
1035+
[
1036+
{
1037+
"step": "install " + package,
1038+
"command": "pip install " + package,
1039+
}
1040+
],
1041+
indent=4,
1042+
)
10181043
file.write(jsonStep)
10191044

10201045
def getLocalPackageVersion(self, packageList):
1046+
'''Get package versions from the local environment. For Python versions
1047+
< 3.8, if the package does not contain an attribute of "__version__",
1048+
"version", or "VERSION", no package version will be found.
10211049
1022-
packageVersion = []
1050+
Parameters
1051+
----------
1052+
packageList : list
1053+
List of Python packages.
1054+
1055+
Returns
1056+
-------
1057+
list
1058+
Nested list of Python package names and found versions.
1059+
'''
1060+
packageAndVersion = []
10231061
if sys.version_info[1] >= 8:
10241062
from importlib.metadata import version
10251063
for package in packageList:
10261064
try:
1027-
packageVersion.append(version(package))
1065+
packageAndVersion.append([package,version(package)])
10281066
except PackageNotFoundError:
10291067
print("Warning: Package {} was not found in the local environment, so a version could not be determined.".format(package))
1030-
print("The pip installation command will not include a version number.")
1031-
packageVersion.append(None)
1032-
return packageVersion
1068+
print("The pip installation command will not include a version number for {}.").format(package)
1069+
packageAndVersion.append([package, None])
1070+
return packageAndVersion
10331071
else:
10341072
import importlib
10351073
for package in packageList:
10361074
name = importlib.import_module(package)
10371075
try:
1038-
packageVersion.append(name.__version__)
1039-
except:
1076+
packageAndVersion.append([package, name.__version__])
1077+
except AttributeError:
1078+
pass
10401079
try:
1041-
packageVersion.append(name.version)
1042-
except:
1080+
packageAndVersion.append([package, name.version])
1081+
except AttributeError:
10431082
try:
1044-
packageVersion.append(name.VERSION)
1045-
except:
1083+
packageAndVersion.append([package, name.VERSION])
1084+
except AttributeError:
10461085
print("Warning: Package {} was not found in the local environment, so a version could not be determined.".format(package))
1047-
print("The pip installation command will not include a version number.")
1048-
return packageVersion
1086+
print("The pip installation command will not include a version number for {}.".format(package))
1087+
packageAndVersion.append([package, None])
1088+
return packageAndVersion
10491089

1050-
def getCodeDependencies(self, jPath):
1090+
def getCodeDependencies(self, jPath=Path.cwd()):
1091+
'''Get the package dependencies for all Python scripts in the
1092+
provided directory path. Note that currently this functionality
1093+
only works for .py files.
1094+
1095+
Parameters
1096+
----------
1097+
jPath : string, optional
1098+
File location for the output JSON file. Default is the current
1099+
working directory.
1100+
1101+
Returns
1102+
-------
1103+
list
1104+
List of found package dependencies.
1105+
'''
10511106
fileNames = []
10521107
fileNames.extend(sorted(Path(jPath).glob("*.py")))
10531108

@@ -1059,14 +1114,31 @@ def getCodeDependencies(self, jPath):
10591114
return importInfo
10601115

10611116
def findImports(self, fPath):
1062-
# modified from https://stackoverflow.com/questions/44988487/regex-to-parse-import-statements-in-python
1117+
'''Find import calls in provided Python code path. Ignores
1118+
built in Python modules.
1119+
1120+
Credit: modified from https://stackoverflow.com/questions/44988487/regex-to-parse-import-statements-in-python
1121+
1122+
Parameters
1123+
----------
1124+
fPath : string
1125+
File location for the Python file to be parsed.
1126+
1127+
Returns
1128+
-------
1129+
list
1130+
List of found package dependencies.
1131+
'''
10631132
import ast
10641133

10651134
fileText = open(fPath).read()
1066-
tree = ast.parse(fileText)
1135+
# Parse the file to get the abstract syntax tree representation
1136+
tree = ast.parse(fileText)
10671137
modules = []
10681138

1139+
# Walk through each node in the ast to find import calls
10691140
for node in ast.walk(tree):
1141+
# Determine parent module for from * import * calls
10701142
if isinstance(node, ast.ImportFrom):
10711143
for name in node.names:
10721144
if not name.asname:
@@ -1078,6 +1150,7 @@ def findImports(self, fPath):
10781150

10791151
modules = list(set(modules))
10801152
try:
1153+
# Remove 'settings' module that is auto generated for SAS Model Manager score code
10811154
modules.remove('settings')
10821155
return modules
10831156
except ValueError:
@@ -1133,7 +1206,7 @@ def getPackageNames(self, stream):
11331206
Generates (module, class_name) tuples from a pickle stream. Extracts all class names referenced
11341207
by GLOBAL and STACK_GLOBAL opcodes.
11351208
1136-
Credit: https://stackoverflow.com/questions/64850179/inspecting-a-pickle-dump-for-dependencies
1209+
Credit: modified from https://stackoverflow.com/questions/64850179/inspecting-a-pickle-dump-for-dependencies
11371210
More information here: https://github.com/python/cpython/blob/main/Lib/pickletools.py
11381211
11391212
Parameters

0 commit comments

Comments
 (0)