@@ -15,6 +15,20 @@ Parameter PackageWithPythonDepsLocation = "package-with-python-deps";
1515
1616Parameter ExportPackageWithPythonDepsLocation = " export-package-with-python-deps" ;
1717
18+ Parameter PublishWithPythonDepsLocation = " publish-with-python-deps" ;
19+
20+ Parameter ExportedPackagesDir = " /home/irisowner/zpm/tests/integration_tests/Test/PM/Integration/_data/python-deps-tests/export-package-with-python-deps/" ;
21+
22+ Method OnAfterOneTest () As %Status
23+ {
24+ // Make test environment clean by uninstalling all modules at the end of each test
25+ set sc = ##class (%IPM.Main ).Shell (" uninstall -all" )
26+ do $$$AssertStatusOK(sc , " Successfully uninstalled all modules after test" )
27+ set sc = ##class (%IPM.Main ).Shell (" repo -delete-all" )
28+ do $$$AssertStatusOK(sc , " Successfully deleted all repositories after test" )
29+ return sc
30+ }
31+
1832/// The PythonWheel resource processor expects to install wheels from the dist directory, as opposed to source code
1933ClassMethod GenerateWheel ()
2034{
@@ -178,25 +192,235 @@ Method TestPackageWithPythonDeps() As %Status
178192 set sc = ##class (%IPM.Main ).Shell (" install -v localrepo/package-with-python-deps" )
179193 do $$$AssertStatusOK(sc , " Installed module from local filesystem repo" )
180194
181- set exportDir = ..GetModuleDir (" python-deps-tests" , ..#ExportPackageWithPythonDepsLocation)
182-
183- set exportTarball = ..GetModuleDir (" python-deps-tests" , ..#ExportPackageWithPythonDepsLocation, " package-with-python-deps" )
195+ set exportPath = ..GetRandomExportPath ()
196+ set exportTarball = exportPath _" .tgz"
184197
185198 // Run package with the python-deps export flag
186- set sc = ##class (%IPM.Main ).Shell (" package package-with-python-deps -v -export-python-deps -path " _ exportTarball )
199+ set sc = ##class (%IPM.Main ).Shell (" package package-with-python-deps -v -export-python-deps -path " _ exportPath )
187200 do $$$AssertStatusOK(sc , " Packaged module with -export-python-deps" )
188201
189202 set sc = ##class (%IPM.Main ).Shell (" uninstall package-with-python-deps" )
190203 do $$$AssertStatusOK(sc , " Uninstalled module" )
191204
192- set sc = ##class (%IPM.Main ).Shell (" load -bypass-py-deps " _ exportDir _ " package-with-python-deps.tgz" )
205+
206+ set wheels = { " wheels" : [" ansible_core-2.19.4-py3-none-any.whl" ,
207+ " ansible_core-2.19.5-py3-none-any.whl" ,
208+ " ansible-12.2.0-py3-none-any.whl" ,
209+ " cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" ,
210+ " cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl" ,
211+ " jinja2-3.1.6-py3-none-any.whl" ,
212+ " lune-1.6.4-py3-none-any.whl" ,
213+ " markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" ,
214+ " packaging-25.0-py3-none-any.whl" ,
215+ " packaging-26.0-py3-none-any.whl" ,
216+ " pycparser-2.23-py3-none-any.whl" ,
217+ " pycparser-3.0-py3-none-any.whl" ,
218+ " pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" ,
219+ " resolvelib-1.2.1-py3-none-any.whl" ] }
220+ do ..AssertWheelFilesExistInPackagedModule (exportTarball , wheels )
221+
222+ set sc = ##class (%IPM.Main ).Shell (" load -bypass-py-deps " _exportTarball )
193223 do $$$AssertStatusOK(sc , " Loaded packaged module" )
194224
195225 // Cleanup
196226 set sc = ##class (%IPM.Main ).Shell (" uninstall package-with-python-deps" )
197227 do $$$AssertStatusOK(sc , " Uninstalled module" )
228+
198229 set sc = ##class (%IPM.Main ).Shell (" repo -delete -n localrepo" )
199230 do $$$AssertStatusOK(sc , " Deleted local filesystem repo" )
200231}
201232
233+ /// Omitting -export-python-deps flag should package without the python
234+ Method TestPackageWithoutPythonDeps () As %Status
235+ {
236+ set dir = ..GetModuleDir (" python-deps-tests" , ..#PackageWithPythonDepsLocation)
237+
238+ // Use a local filesystem repo to install the test module into the namespace
239+ set sc = ##class (%IPM.Main ).Shell (" repo -fs -name localrepo -path " _ dir )
240+ do $$$AssertStatusOK(sc , " Configured local filesystem repo" )
241+
242+ set sc = ##class (%IPM.Main ).Shell (" install -v localrepo/package-with-python-deps" )
243+ do $$$AssertStatusOK(sc , " Installed module from local filesystem repo" )
244+
245+ set exportPath = ..GetRandomExportPath ()
246+ set exportTarball = exportPath _" .tgz"
247+
248+ // Run package without the -export-python-deps flag
249+ set sc = ##class (%IPM.Main ).Shell (" package package-with-python-deps -v -path " _ exportPath )
250+ do $$$AssertStatusOK(sc , " Packaged module without -export-python-deps" )
251+
252+ set sc = ##class (%IPM.Main ).Shell (" uninstall package-with-python-deps" )
253+ do $$$AssertStatusOK(sc , " Uninstalled module" )
254+
255+
256+ set wheels = { " wheels" : [" ansible_core-2.19.4-py3-none-any.whl" ,
257+ " ansible_core-2.19.5-py3-none-any.whl" ,
258+ " ansible-12.2.0-py3-none-any.whl" ,
259+ " cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" ,
260+ " cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl" ,
261+ " jinja2-3.1.6-py3-none-any.whl" ,
262+ " lune-1.6.4-py3-none-any.whl" ,
263+ " markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" ,
264+ " packaging-25.0-py3-none-any.whl" ,
265+ " packaging-26.0-py3-none-any.whl" ,
266+ " pycparser-2.23-py3-none-any.whl" ,
267+ " pycparser-3.0-py3-none-any.whl" ,
268+ " pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" ,
269+ " resolvelib-1.2.1-py3-none-any.whl" ] }
270+ do ..AssertWheelFilesExistInPackagedModule (exportTarball , wheels , 0 )
271+
272+ set sc = ##class (%IPM.Main ).Shell (" repo -delete -n localrepo" )
273+ do $$$AssertStatusOK(sc , " Deleted local filesystem repo" )
274+ }
275+
276+ /// Test publishing with the -export-python-deps flag
277+ Method TestPublishWithPythonDeps () As %Status
278+ {
279+
280+ // 1. Test the case of python dependencies defined via requirements.txt
281+ set moduleNameReq = " publish-with-python-just-reqs"
282+ set wheelsReq = [" docx2md-1.0.5-py3-none-any.whl" ,
283+ " lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl" ,
284+ " pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl" ]
285+ set wheels = {}
286+ do wheels .%Set (moduleNameReq , wheelsReq )
287+ do ..PublishTestHandler (moduleNameReq , wheels )
288+
289+ // 2. Test the case of python dependencies defined via wheel resources in module.xml
290+ set moduleNameWhl = " publish-with-python-just-wheels"
291+ set wheelsWhl = [" numpy-2.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl" ]
292+ set wheels = {}
293+ do wheels .%Set (moduleNameWhl , wheelsWhl )
294+ do ..PublishTestHandler (moduleNameWhl , wheels )
295+
296+ // 3. Test the case of python dependencies both defined in module.xml and requirements.txt
297+ set moduleNameWhlReq = " publish-with-python-wheels-and-reqs"
298+ set wheelsWhlReq = [" colorama-0.4.6-py2.py3-none-any.whl" ,
299+ " six-1.17.0-py2.py3-none-any.whl" ]
300+ set wheels = {}
301+ do wheels .%Set (moduleNameWhlReq , wheelsWhlReq )
302+ do ..PublishTestHandler (moduleNameWhlReq , wheels )
303+
304+
305+
306+ // 4. Test the three cases above but as dependencies of a base module being installed
307+ set moduleNameDeps = " publish-with-python-deps-base"
308+ // There are no wheels defined for this base module but each of the dependencies do have wheels to check
309+ set wheels = {}
310+ do wheels .%Set (moduleNameReq , wheelsReq )
311+ do wheels .%Set (moduleNameWhl , wheelsWhl )
312+ do wheels .%Set (moduleNameWhlReq , wheelsWhlReq )
313+ do ..PublishTestHandler (moduleNameDeps , wheels )
314+ }
315+
316+ /// Handler method for publish test suite
317+ Method PublishTestHandler (
318+ moduleName As %String ,
319+ wheels As %DynamicObject )
320+ {
321+ // Create a filesystem repository
322+ set dir = ..GetModuleDir (" python-deps-tests" , ..#PublishWithPythonDepsLocation)
323+ set sc = ##class (%IPM.Main ).Shell (" repo -fs -name localrepo -path " _dir )
324+ do $$$AssertStatusOK(sc , " Configured local filesystem repo" )
325+
326+ // Install module from local filesystem repo
327+ set sc = ##class (%IPM.Main ).Shell (" install -v localrepo/" _moduleName )
328+ do $$$AssertStatusOK(sc , " Installed module from local filesystem repo" )
329+
330+ // Create a remote repository for publishing
331+ set sc = ##class (%IPM.Main ).Shell (" repo -r -name remote -url http://registry:52773/registry -username admin -password SYS" )
332+ do $$$AssertStatusOK(sc , " Configured remote repo" )
333+
334+ // Publish module to remote repository
335+ set sc = ##class (%IPM.Main ).Shell (" publish -v " _moduleName _" -r remote -export-python-deps -export-deps 1" )
336+ do $$$AssertStatusOK(sc , " Published module " _moduleName _" to remote repository" )
337+
338+ // Uninstall module that came from filesystem repository
339+ set sc = ##class (%IPM.Main ).Shell (" uninstall " _moduleName )
340+ do $$$AssertStatusOK(sc , " Uninstalled module successfully" )
341+
342+ // Install published module from remote repository
343+ set sc = ##class (%IPM.Main ).Shell (" install -v remote/" _moduleName )
344+ do $$$AssertStatusOK(sc , " Successfully installed published module " _moduleName _" from remote repository" )
345+
346+ // Confirm wheels were included in packaging/publishing
347+ do ..AssertWheelResourcesExistForModule (wheels )
348+
349+ // Uninstall the module used for this test
350+ set sc = ##class (%IPM.Main ).Shell (" uninstall " _moduleName )
351+ do $$$AssertStatusOK(sc , " Uninstalled module " _moduleName _" successfully" )
352+
353+ // Remove the repositories used for this test
354+ set sc = ##class (%IPM.Main ).Shell (" repo -delete -n localrepo" )
355+ do $$$AssertStatusOK(sc , " Removed local filesystem repo" )
356+ set sc = ##class (%IPM.Main ).Shell (" repo -delete -n remote" )
357+ do $$$AssertStatusOK(sc , " Removed remote repo" )
358+ }
359+
360+ /// Used in package and publish tests to get a known, random directory path for where module gets packaged to
361+ Method GetRandomExportPath ()
362+ {
363+ set randomDir = $translate ($system .Encryption .Base64Encode ($system .Encryption .GenCryptRand (6 ))," +/=" )
364+ return ..#ExportedPackagesDir_randomDir _" /package"
365+ }
366+
367+ /// Checks to see if the physical files exist in a packaged module
368+ ///
369+ /// Arguments:
370+ /// - exportTarball: path to the packaged tarball
371+ /// - wheels: Object of <relative path>-<wheel name> pairs array of file names to check for in the unpackaged module
372+ /// Ex: { "/": ["wheel-1.whl","wheel-2.whl"], "wheels":["wheel-3.whl","wheel-4.whl"] }
373+ /// - doesExist: set to 0 if we want to check that files explicitly do not exist (in the case -export-python-deps was not provided)
374+ Method AssertWheelFilesExistInPackagedModule (
375+ exportTarball As %String ,
376+ wheels As %DynamicObject ,
377+ doesExist As %Boolean = 1 )
378+ {
379+ set exportDir = ##class (%File ).GetDirectory (exportTarball )
380+ set sc = ##class (%IPM.General.Archive ).Extract (exportTarball , exportDir )
381+ do $$$AssertStatusOK(sc , " Extracted packaged tarball contents to scan for wheel files" )
382+
383+
384+ set wheelsIter = wheels .%GetIterator ()
385+ while wheelsIter .%GetNext (.relativePath , .wheelArr ) {
386+ set directory = ##class (%File ).NormalizeDirectory (relativePath , exportDir )
387+ set wheelArrIter = wheelArr .%GetIterator ()
388+ while wheelArrIter .%GetNext (, .wheelName ) {
389+ set fileName = ##class (%File ).NormalizeFilename (wheelName , directory )
390+ set exists = ##class (%File ).Exists (fileName )
391+ if (doesExist ) {
392+ do $$$AssertTrue(exists , wheelName _" exists in packaged contents" )
393+ }
394+ else {
395+ do $$$AssertNotTrue(exists , wheelName _" not included in packaged contents" )
396+ }
397+ }
398+ }
399+ }
400+
401+ /// Given an object of <module name>-<wheel list> pairs,
402+ /// compiles a list of resources for the module and iterates over the wheel list to check if that wheel is a resource of the module
403+ ///
404+ /// Ex: wheels = { "module1": ["wheel-1.whl","wheel-2.whl"], "module2":["wheel-3.whl","wheel-4.whl"] }
405+ Method AssertWheelResourcesExistForModule (wheels As %DynamicObject )
406+ {
407+ set wheelsIter = wheels .%GetIterator ()
408+ while wheelsIter .%GetNext (.moduleName , .moduleWheels ) {
409+ set module = ##class (%IPM.Storage.Module ).NameOpen (moduleName )
410+ set resourceList = " "
411+ set key = " "
412+ for {
413+ set resource = module .Resources .GetNext (.key )
414+ quit :key =" "
415+ set resourceList = resourceList _$listbuild ($zconvert (resource .Name , " l" ))
416+ }
417+
418+ set moduleWheelsIter = moduleWheels .%GetIterator ()
419+ while moduleWheelsIter .%GetNext (, .wheel ) {
420+ set wheelIsResource = ($listfind (resourceList , wheel ) > 0 )
421+ do $$$AssertTrue(wheelIsResource , " Wheel resource " _wheel _" is a part of the installed module " _moduleName )
422+ }
423+ }
424+ }
425+
202426}
0 commit comments