55require "rubygems"
66require "fileutils"
77require "tmpdir"
8+ require "json"
89
910# Support for python packages.
1011#
@@ -324,9 +325,9 @@ def process_maintainer(headers)
324325 # * The path to a python sdist file ending in .tar.gz
325326 # * The path to a python wheel file ending in .whl
326327 def input ( package )
327- if attributes [ :python_obey_requirements_txt? ]
328- raise "--python-obey-requirements-txt is temporarily unsupported at this time."
329- end
328+ # if attributes[:python_obey_requirements_txt?]
329+ # raise "--python-obey-requirements-txt is temporarily unsupported at this time."
330+ # end
330331 explore_environment
331332
332333 path_to_package = download_if_necessary ( package , version )
@@ -337,6 +338,10 @@ def input(package)
337338 logger . error ( "The path doesn't appear to be a python package directory. I expected either a pypackage.toml or setup.py but found neither." , :package => package )
338339 raise "Unable to find python package; tried #{ setup_py } "
339340 end
341+
342+ if attributes [ :python_obey_requirements_txt? ] && File . exist? ( File . join ( path_to_package , "requirements.txt" ) )
343+ @requirements_txt = File . read ( File . join ( path_to_package , "requirements.txt" ) )
344+ end
340345 end
341346
342347 if File . file? ( path_to_package )
@@ -355,7 +360,7 @@ def input(package)
355360 log . error ( "Failed building python package wheel format. This might be a bug in fpm." )
356361 raise "Failed building python package format."
357362 end
358- else File . directory? ( path_to_package )
363+ elsif File . directory? ( path_to_package )
359364 logger . debug ( "Found directory and assuming it's a python source package." )
360365 safesystem ( *attributes [ :python_pip ] , "wheel" , "--no-deps" , "-w" , build_path , path_to_package )
361366
@@ -454,11 +459,12 @@ def download_if_necessary(package, version=nil)
454459
455460 safesystem ( *setup_cmd )
456461
457- files = ::Dir . glob ( File . join ( target , "*.{whl,tar.gz,zip}" ) )
462+ #files = ::Dir.glob(File.join(target, "*.{whl,tar.gz,zip}"))
463+ files = ::Dir . entries ( target ) . filter { |entry | entry =~ /\. (whl|tar\. gz|zip)$/ }
458464 if files . length != 1
459465 raise "Unexpected directory layout after `pip download ...`. This might be an fpm bug? The directory contains these files: #{ files . inspect } "
460466 end
461- return files . first
467+ return File . join ( target , files . first )
462468 else
463469 # no pip, use easy_install
464470 logger . debug ( "no pip, defaulting to easy_install" , :easy_install => attributes [ :python_easyinstall ] )
@@ -467,7 +473,8 @@ def download_if_necessary(package, version=nil)
467473 "--build-directory" , target , want_pkg )
468474 # easy_install will put stuff in @tmpdir/packagename/, so find that:
469475 # @tmpdir/somepackage/setup.py
470- dirs = ::Dir . glob ( File . join ( target , "*" ) )
476+ #dirs = ::Dir.glob(File.join(target, "*"))
477+ files = ::Dir . entries ( target ) . filter { |entry | entry != "." && entry != ".." }
471478 if dirs . length != 1
472479 raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{ build_path } "
473480 end
@@ -511,52 +518,46 @@ def load_package_info(path)
511518 self . maintainer = metadata . maintainer
512519
513520 if !attributes [ :no_auto_depends? ] and attributes [ :python_dependencies? ]
514- sys_platform = nil
515- execmd ( [ attributes [ :python_bin ] , "-c" , "import sys; print(sys.platform)" ] , :stdin => false , :stderr => false ) do |stdout |
516- sys_platform = stdout . read . chomp
517- end
518-
519- dep_re = /^([^<>!= ]+)\s *(?:([~<>!=]{1,2})\s *(.*))?$/
520-
521521 # Python Dependency specifiers are a somewhat complex format described here:
522522 # https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers
523523 #
524- # It would be ideal to support the entire specifier format, but it's unclear if that's necessary
525- # for most packaging situations.
526- #
527- # If a specifier is found to not work under fpm, please file an issue on the fpm issue tracker
528- # and hopefully support for it can be added.
524+ # We can ask python's packaging module to parse and evaluate these.
525+ # XXX: Allow users to override environnment values.
529526 #
530527 # Example:
531528 # Requires-Dist: tzdata; sys_platform = win32
532529 # Requires-Dist: asgiref>=3.8.1
533530
534- metadata . requires . each do |text |
535- dep , environment = text . split ( / *; */ )
531+ dep_re = /^([^<>!= ]+)\s *(?:([~<>!=]{1,2})\s *(.*))?$/
532+
533+ reqs = [ ]
534+
535+ # --python-obey-requirements-txt should replace the requirments listed from the metadata
536+ if attributes [ :python_obey_requirements_txt? ] && !@requirements_txt . nil?
537+ requires = @requirements_txt . split ( "\n " )
538+ else
539+ requires = metadata . requires
540+ end
541+
542+ # Evaluate python package requirements and only show ones matching the current environment
543+ # (Environment markers, etc)
544+ # Additionally, 'extra' features such as a requirement named `django[bcrypt]` isn't quite supported yet,
545+ # since the marker.evaluate() needs to be passed some environment like { "extra": "bcrypt" }
546+ execmd ( [ attributes [ :python_bin ] , File . expand_path ( File . join ( "pyfpm" , "parse_requires.py" ) , File . dirname ( __FILE__ ) ) ] ) do |stdin , stdout , stderr |
547+ requires . each { |r | stdin . puts ( r ) }
548+ stdin . close
549+ data = stdout . read
550+ logger . pipe ( stderr => :warn )
551+ reqs += JSON . parse ( data )
552+ end
553+
554+ reqs . each do |dep |
536555 match = dep_re . match ( dep )
537556 if match . nil?
538557 logger . error ( "Unable to parse dependency" , :dependency => dep )
539558 raise FPM ::InvalidPackageConfiguration , "Invalid dependency '#{ dep } '"
540559 end
541560
542- if environment
543- if environment . include? ( "sys_platform ==" ) && !environment . include? ( "sys_platform == #{ sys_platform } " )
544- logger . debug ( "Ignoring dependency because it doesn't match the current platform" , :current => sys_platform , :target => environment , :dependency => text )
545- next
546- end
547-
548- if environment . include? ( "extra ==" )
549- logger . debug ( "Ignoring extra/optional dependency" , :dependency => text , :extra => environment )
550- next
551- end
552-
553- unsupported_markers = UNSUPPORTED_DEPENDENCY_MARKERS . filter { |m | environment . include? ( m ) }
554- if unsupported_markers . any?
555- logger . debug ( "Package contains an unsupported Requires-Dist 'environment marker' which fpm doesn't yet support. If you want support for these, please file an issue." , :markers => unsupported_markers , :dependency => text )
556- next
557- end
558- end # if environment
559-
560561 name , cmp , version = match . captures
561562
562563 next if attributes [ :python_disable_dependency ] . include? ( name )
@@ -593,7 +594,7 @@ def fix_name(name)
593594 if name . start_with? ( "python" )
594595 # If the python package is called "python-foo" strip the "python-" part while
595596 # prepending the package name prefix.
596- return [ attributes [ :python_package_name_prefix ] , name . gsub ( /^python-/ , "" ) ] . join ( "-" )
597+ return [ attributes [ :ptython_package_name_prefix ] , name . gsub ( /^python-/ , "" ) ] . join ( "-" )
597598 else
598599 return [ attributes [ :python_package_name_prefix ] , name ] . join ( "-" )
599600 end
0 commit comments