dependabot-python 0.212.0 → 0.214.0
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.
- checksums.yaml +4 -4
 - data/helpers/build +5 -6
 - data/helpers/lib/parser.py +59 -0
 - data/helpers/requirements.txt +3 -3
 - data/helpers/run.py +2 -0
 - data/lib/dependabot/python/file_fetcher.rb +21 -12
 - data/lib/dependabot/python/file_parser/{poetry_files_parser.rb → pyproject_files_parser.rb} +84 -2
 - data/lib/dependabot/python/file_parser/setup_file_parser.rb +4 -4
 - data/lib/dependabot/python/file_parser.rb +5 -29
 - data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +7 -22
 - data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +11 -8
 - data/lib/dependabot/python/file_updater/pipfile_preparer.rb +6 -4
 - data/lib/dependabot/python/file_updater/poetry_file_updater.rb +15 -7
 - data/lib/dependabot/python/file_updater/pyproject_preparer.rb +16 -1
 - data/lib/dependabot/python/file_updater.rb +14 -1
 - data/lib/dependabot/python/helpers.rb +37 -0
 - data/lib/dependabot/python/metadata_finder.rb +2 -0
 - data/lib/dependabot/python/python_versions.rb +11 -7
 - data/lib/dependabot/python/requirement.rb +7 -4
 - data/lib/dependabot/python/requirement_parser.rb +20 -23
 - data/lib/dependabot/python/update_checker/index_finder.rb +1 -1
 - data/lib/dependabot/python/update_checker/latest_version_finder.rb +2 -2
 - data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +19 -21
 - data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +16 -18
 - data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +14 -12
 - data/lib/dependabot/python/update_checker/requirements_updater.rb +17 -4
 - data/lib/dependabot/python/update_checker.rb +82 -25
 - data/lib/dependabot/python/version.rb +2 -2
 - metadata +15 -56
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 940ed0c4abf7f4d3a496321e4898ba9c123091d6539f86ef54d7ee74dadf3344
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 802abe558f75bc2e98f1b88e93be85fc48f8b71774a1ff37b8ea16311381f587
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 523ff39717afd9636f3d2f3115d6953817ab01585e2e218233eb0439a7cc9e5ac620c4b28d429b35256530a32bff6e71a73ffdfd72587ba53c8b10b6a3070175
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a3d05a60ad4d1b08dfe8fed7cdac12384aa49fbb3ad130008bf4748ab710df9b20a8297c99f66e33bd672717b52be32c17434b2ed253fe4bb6556cfc87941b05
         
     | 
    
        data/helpers/build
    CHANGED
    
    | 
         @@ -18,9 +18,8 @@ cp -r \ 
     | 
|
| 
       18 
18 
     | 
    
         
             
              "$install_dir"
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
            cd "$install_dir"
         
     | 
| 
       21 
     | 
    
         
            -
            PYENV_VERSION=3. 
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
            PYENV_VERSION=3.10.5 pyenv exec poetry config experimental.new-installer false
         
     | 
| 
      
 21 
     | 
    
         
            +
            PYENV_VERSION=3.11.0 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
         
     | 
| 
      
 22 
     | 
    
         
            +
            PYENV_VERSION=3.10.8 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
         
     | 
| 
      
 23 
     | 
    
         
            +
            PYENV_VERSION=3.9.15 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
         
     | 
| 
      
 24 
     | 
    
         
            +
            PYENV_VERSION=3.8.15 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
         
     | 
| 
      
 25 
     | 
    
         
            +
            PYENV_VERSION=3.7.15 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
         
     | 
    
        data/helpers/lib/parser.py
    CHANGED
    
    | 
         @@ -11,11 +11,70 @@ from pip._internal.req.constructors import ( 
     | 
|
| 
       11 
11 
     | 
    
         
             
                install_req_from_line,
         
     | 
| 
       12 
12 
     | 
    
         
             
                install_req_from_parsed_requirement,
         
     | 
| 
       13 
13 
     | 
    
         
             
            )
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            from packaging.requirements import InvalidRequirement, Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
            import toml
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       14 
18 
     | 
    
         
             
            # Inspired by pips internal check:
         
     | 
| 
       15 
19 
     | 
    
         
             
            # https://github.com/pypa/pip/blob/0bb3ac87f5bb149bd75cceac000844128b574385/src/pip/_internal/req/req_file.py#L35
         
     | 
| 
       16 
20 
     | 
    
         
             
            COMMENT_RE = re.compile(r'(^|\s+)#.*$')
         
     | 
| 
       17 
21 
     | 
    
         | 
| 
       18 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
            def parse_pep621_dependencies(pyproject_path):
         
     | 
| 
      
 24 
     | 
    
         
            +
                project_toml = toml.load(pyproject_path)['project']
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def parse_toml_section_pep621_dependencies(pyproject_path, dependencies):
         
     | 
| 
      
 27 
     | 
    
         
            +
                    requirement_packages = []
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    def version_from_req(specifier_set):
         
     | 
| 
      
 30 
     | 
    
         
            +
                        if (len(specifier_set) == 1 and
         
     | 
| 
      
 31 
     | 
    
         
            +
                                next(iter(specifier_set)).operator in {"==", "==="}):
         
     | 
| 
      
 32 
     | 
    
         
            +
                            return next(iter(specifier_set)).version
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    for dependency in dependencies:
         
     | 
| 
      
 35 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 36 
     | 
    
         
            +
                            req = Requirement(dependency)
         
     | 
| 
      
 37 
     | 
    
         
            +
                        except InvalidRequirement as e:
         
     | 
| 
      
 38 
     | 
    
         
            +
                            print(json.dumps({"error": repr(e)}))
         
     | 
| 
      
 39 
     | 
    
         
            +
                            exit(1)
         
     | 
| 
      
 40 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 41 
     | 
    
         
            +
                            requirement_packages.append({
         
     | 
| 
      
 42 
     | 
    
         
            +
                                "name": req.name,
         
     | 
| 
      
 43 
     | 
    
         
            +
                                "version": version_from_req(req.specifier),
         
     | 
| 
      
 44 
     | 
    
         
            +
                                "markers": str(req.marker) or None,
         
     | 
| 
      
 45 
     | 
    
         
            +
                                "file": pyproject_path,
         
     | 
| 
      
 46 
     | 
    
         
            +
                                "requirement": str(req.specifier),
         
     | 
| 
      
 47 
     | 
    
         
            +
                                "extras": sorted(list(req.extras))
         
     | 
| 
      
 48 
     | 
    
         
            +
                            })
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    return requirement_packages
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                dependencies = []
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                if 'dependencies' in project_toml:
         
     | 
| 
      
 55 
     | 
    
         
            +
                    dependencies_toml = project_toml['dependencies']
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    runtime_dependencies = parse_toml_section_pep621_dependencies(
         
     | 
| 
      
 58 
     | 
    
         
            +
                        pyproject_path,
         
     | 
| 
      
 59 
     | 
    
         
            +
                        dependencies_toml
         
     | 
| 
      
 60 
     | 
    
         
            +
                    )
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    dependencies.extend(runtime_dependencies)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                if 'optional-dependencies' in project_toml:
         
     | 
| 
      
 65 
     | 
    
         
            +
                    optional_dependencies_toml = project_toml['optional-dependencies']
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    for group in optional_dependencies_toml:
         
     | 
| 
      
 68 
     | 
    
         
            +
                        group_dependencies = parse_toml_section_pep621_dependencies(
         
     | 
| 
      
 69 
     | 
    
         
            +
                            pyproject_path,
         
     | 
| 
      
 70 
     | 
    
         
            +
                            optional_dependencies_toml[group]
         
     | 
| 
      
 71 
     | 
    
         
            +
                        )
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                        dependencies.extend(group_dependencies)
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                return json.dumps({"result": dependencies})
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
       19 
78 
     | 
    
         
             
            def parse_requirements(directory):
         
     | 
| 
       20 
79 
     | 
    
         
             
                # Parse the requirements.txt
         
     | 
| 
       21 
80 
     | 
    
         
             
                requirement_packages = []
         
     | 
    
        data/helpers/requirements.txt
    CHANGED
    
    | 
         @@ -1,10 +1,10 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            pip>=21.3.1,<22. 
     | 
| 
       2 
     | 
    
         
            -
            pip-tools>=6.4.0,<6. 
     | 
| 
      
 1 
     | 
    
         
            +
            pip>=21.3.1,<22.4.0  # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
         
     | 
| 
      
 2 
     | 
    
         
            +
            pip-tools>=6.4.0,<6.10.1  # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
         
     | 
| 
       3 
3 
     | 
    
         
             
            flake8==5.0.4
         
     | 
| 
       4 
4 
     | 
    
         
             
            hashin==0.17.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            pipenv==2022.4.8
         
     | 
| 
       6 
6 
     | 
    
         
             
            pipfile==0.0.2
         
     | 
| 
       7 
     | 
    
         
            -
            poetry>=1.1.15 
     | 
| 
      
 7 
     | 
    
         
            +
            poetry>=1.1.15,<1.3.0
         
     | 
| 
       8 
8 
     | 
    
         
             
            wheel==0.37.1
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
            # Some dependencies will only install if Cython is present
         
     | 
    
        data/helpers/run.py
    CHANGED
    
    | 
         @@ -10,6 +10,8 @@ if __name__ == "__main__": 
     | 
|
| 
       10 
10 
     | 
    
         
             
                    print(parser.parse_requirements(args["args"][0]))
         
     | 
| 
       11 
11 
     | 
    
         
             
                elif args["function"] == "parse_setup":
         
     | 
| 
       12 
12 
     | 
    
         
             
                    print(parser.parse_setup(args["args"][0]))
         
     | 
| 
      
 13 
     | 
    
         
            +
                elif args["function"] == "parse_pep621_dependencies":
         
     | 
| 
      
 14 
     | 
    
         
            +
                    print(parser.parse_pep621_dependencies(args["args"][0]))
         
     | 
| 
       13 
15 
     | 
    
         
             
                elif args["function"] == "get_dependency_hash":
         
     | 
| 
       14 
16 
     | 
    
         
             
                    print(hasher.get_dependency_hash(*args["args"]))
         
     | 
| 
       15 
17 
     | 
    
         
             
                elif args["function"] == "get_pipfile_hash":
         
     | 
| 
         @@ -5,15 +5,15 @@ require "toml-rb" 
     | 
|
| 
       5 
5 
     | 
    
         
             
            require "dependabot/file_fetchers"
         
     | 
| 
       6 
6 
     | 
    
         
             
            require "dependabot/file_fetchers/base"
         
     | 
| 
       7 
7 
     | 
    
         
             
            require "dependabot/python/requirement_parser"
         
     | 
| 
       8 
     | 
    
         
            -
            require "dependabot/python/file_parser/ 
     | 
| 
      
 8 
     | 
    
         
            +
            require "dependabot/python/file_parser/pyproject_files_parser"
         
     | 
| 
       9 
9 
     | 
    
         
             
            require "dependabot/errors"
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            module Dependabot
         
     | 
| 
       12 
12 
     | 
    
         
             
              module Python
         
     | 
| 
       13 
13 
     | 
    
         
             
                class FileFetcher < Dependabot::FileFetchers::Base
         
     | 
| 
       14 
     | 
    
         
            -
                  CHILD_REQUIREMENT_REGEX = /^-r\s?(?<path>.*\.(?:txt|in)) 
     | 
| 
       15 
     | 
    
         
            -
                  CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in)) 
     | 
| 
       16 
     | 
    
         
            -
                  DEPENDENCY_TYPES = %w(packages dev-packages) 
     | 
| 
      
 14 
     | 
    
         
            +
                  CHILD_REQUIREMENT_REGEX = /^-r\s?(?<path>.*\.(?:txt|in))/
         
     | 
| 
      
 15 
     | 
    
         
            +
                  CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in))/
         
     | 
| 
      
 16 
     | 
    
         
            +
                  DEPENDENCY_TYPES = %w(packages dev-packages)
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                  def self.required_files_in?(filenames)
         
     | 
| 
       19 
19 
     | 
    
         
             
                    return true if filenames.any? { |name| name.end_with?(".txt", ".in") }
         
     | 
| 
         @@ -24,7 +24,7 @@ module Dependabot 
     | 
|
| 
       24 
24 
     | 
    
         
             
                    # If this repo is using a Pipfile return true
         
     | 
| 
       25 
25 
     | 
    
         
             
                    return true if filenames.include?("Pipfile")
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                    # If this repo is using  
     | 
| 
      
 27 
     | 
    
         
            +
                    # If this repo is using pyproject.toml return true
         
     | 
| 
       28 
28 
     | 
    
         
             
                    return true if filenames.include?("pyproject.toml")
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                    return true if filenames.include?("setup.py")
         
     | 
| 
         @@ -69,7 +69,7 @@ module Dependabot 
     | 
|
| 
       69 
69 
     | 
    
         
             
                  end
         
     | 
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
                  def pyproject_files
         
     | 
| 
       72 
     | 
    
         
            -
                    [pyproject, pyproject_lock, poetry_lock].compact
         
     | 
| 
      
 72 
     | 
    
         
            +
                    [pyproject, pyproject_lock, poetry_lock, pdm_lock].compact
         
     | 
| 
       73 
73 
     | 
    
         
             
                  end
         
     | 
| 
       74 
74 
     | 
    
         | 
| 
       75 
75 
     | 
    
         
             
                  def requirement_files
         
     | 
| 
         @@ -81,7 +81,12 @@ module Dependabot 
     | 
|
| 
       81 
81 
     | 
    
         
             
                  end
         
     | 
| 
       82 
82 
     | 
    
         | 
| 
       83 
83 
     | 
    
         
             
                  def check_required_files_present
         
     | 
| 
       84 
     | 
    
         
            -
                    return if requirements_txt_files.any? || 
     | 
| 
      
 84 
     | 
    
         
            +
                    return if requirements_txt_files.any? ||
         
     | 
| 
      
 85 
     | 
    
         
            +
                              requirements_in_files.any? ||
         
     | 
| 
      
 86 
     | 
    
         
            +
                              setup_file ||
         
     | 
| 
      
 87 
     | 
    
         
            +
                              setup_cfg_file ||
         
     | 
| 
      
 88 
     | 
    
         
            +
                              pipfile ||
         
     | 
| 
      
 89 
     | 
    
         
            +
                              pyproject
         
     | 
| 
       85 
90 
     | 
    
         | 
| 
       86 
91 
     | 
    
         
             
                    path = Pathname.new(File.join(directory, "requirements.txt")).
         
     | 
| 
       87 
92 
     | 
    
         
             
                           cleanpath.to_path
         
     | 
| 
         @@ -136,6 +141,10 @@ module Dependabot 
     | 
|
| 
       136 
141 
     | 
    
         
             
                    @poetry_lock ||= fetch_file_if_present("poetry.lock")
         
     | 
| 
       137 
142 
     | 
    
         
             
                  end
         
     | 
| 
       138 
143 
     | 
    
         | 
| 
      
 144 
     | 
    
         
            +
                  def pdm_lock
         
     | 
| 
      
 145 
     | 
    
         
            +
                    @pdm_lock ||= fetch_file_if_present("pdm.lock")
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
       139 
148 
     | 
    
         
             
                  def requirements_txt_files
         
     | 
| 
       140 
149 
     | 
    
         
             
                    req_txt_and_in_files.select { |f| f.name.end_with?(".txt") }
         
     | 
| 
       141 
150 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -169,7 +178,7 @@ module Dependabot 
     | 
|
| 
       169 
178 
     | 
    
         
             
                    repo_contents.
         
     | 
| 
       170 
179 
     | 
    
         
             
                      select { |f| f.type == "file" }.
         
     | 
| 
       171 
180 
     | 
    
         
             
                      select { |f| f.name.end_with?(".txt", ".in") }.
         
     | 
| 
       172 
     | 
    
         
            -
                      reject { |f| f.size >  
     | 
| 
      
 181 
     | 
    
         
            +
                      reject { |f| f.size > 500_000 }.
         
     | 
| 
       173 
182 
     | 
    
         
             
                      map { |f| fetch_file_from_host(f.name) }.
         
     | 
| 
       174 
183 
     | 
    
         
             
                      select { |f| requirements_file?(f) }.
         
     | 
| 
       175 
184 
     | 
    
         
             
                      each { |f| @req_txt_and_in_files << f }
         
     | 
| 
         @@ -189,7 +198,7 @@ module Dependabot 
     | 
|
| 
       189 
198 
     | 
    
         
             
                    repo_contents(dir: relative_reqs_dir).
         
     | 
| 
       190 
199 
     | 
    
         
             
                      select { |f| f.type == "file" }.
         
     | 
| 
       191 
200 
     | 
    
         
             
                      select { |f| f.name.end_with?(".txt", ".in") }.
         
     | 
| 
       192 
     | 
    
         
            -
                      reject { |f| f.size >  
     | 
| 
      
 201 
     | 
    
         
            +
                      reject { |f| f.size > 500_000 }.
         
     | 
| 
       193 
202 
     | 
    
         
             
                      map { |f| fetch_file_from_host("#{relative_reqs_dir}/#{f.name}") }.
         
     | 
| 
       194 
203 
     | 
    
         
             
                      select { |f| requirements_file?(f) }
         
     | 
| 
       195 
204 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -291,8 +300,8 @@ module Dependabot 
     | 
|
| 
       291 
300 
     | 
    
         
             
                          fetch_submodules: true
         
     | 
| 
       292 
301 
     | 
    
         
             
                        ).tap { |f| f.support_file = true }
         
     | 
| 
       293 
302 
     | 
    
         
             
                      rescue Dependabot::DependencyFileNotFound
         
     | 
| 
       294 
     | 
    
         
            -
                        # For  
     | 
| 
       295 
     | 
    
         
            -
                        # given path instead of a setup.py. We do not require a
         
     | 
| 
      
 303 
     | 
    
         
            +
                        # For projects with pyproject.toml attempt to fetch a pyproject.toml
         
     | 
| 
      
 304 
     | 
    
         
            +
                        # at the given path instead of a setup.py. We do not require a
         
     | 
| 
       296 
305 
     | 
    
         
             
                        # setup.py to be present, so if none can be found, simply return
         
     | 
| 
       297 
306 
     | 
    
         
             
                        return [] unless allow_pyproject
         
     | 
| 
       298 
307 
     | 
    
         | 
| 
         @@ -390,7 +399,7 @@ module Dependabot 
     | 
|
| 
       390 
399 
     | 
    
         
             
                    return [] unless pyproject
         
     | 
| 
       391 
400 
     | 
    
         | 
| 
       392 
401 
     | 
    
         
             
                    paths = []
         
     | 
| 
       393 
     | 
    
         
            -
                    Dependabot::Python::FileParser:: 
     | 
| 
      
 402 
     | 
    
         
            +
                    Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |dep_type|
         
     | 
| 
       394 
403 
     | 
    
         
             
                      next unless parsed_pyproject.dig("tool", "poetry", dep_type)
         
     | 
| 
       395 
404 
     | 
    
         | 
| 
       396 
405 
     | 
    
         
             
                      parsed_pyproject.dig("tool", "poetry", dep_type).each do |_, req|
         
     | 
| 
         @@ -12,7 +12,7 @@ require "dependabot/python/name_normaliser" 
     | 
|
| 
       12 
12 
     | 
    
         
             
            module Dependabot
         
     | 
| 
       13 
13 
     | 
    
         
             
              module Python
         
     | 
| 
       14 
14 
     | 
    
         
             
                class FileParser
         
     | 
| 
       15 
     | 
    
         
            -
                  class  
     | 
| 
      
 15 
     | 
    
         
            +
                  class PyprojectFilesParser
         
     | 
| 
       16 
16 
     | 
    
         
             
                    POETRY_DEPENDENCY_TYPES = %w(dependencies dev-dependencies).freeze
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                    # https://python-poetry.org/docs/dependency-specification/
         
     | 
| 
         @@ -25,7 +25,7 @@ module Dependabot 
     | 
|
| 
       25 
25 
     | 
    
         
             
                    def dependency_set
         
     | 
| 
       26 
26 
     | 
    
         
             
                      dependency_set = Dependabot::FileParsers::Base::DependencySet.new
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                      dependency_set += pyproject_dependencies
         
     | 
| 
      
 28 
     | 
    
         
            +
                      dependency_set += pyproject_dependencies if using_poetry? || using_pep621?
         
     | 
| 
       29 
29 
     | 
    
         
             
                      dependency_set += lockfile_dependencies if lockfile
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                      dependency_set
         
     | 
| 
         @@ -36,6 +36,14 @@ module Dependabot 
     | 
|
| 
       36 
36 
     | 
    
         
             
                    attr_reader :dependency_files
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                    def pyproject_dependencies
         
     | 
| 
      
 39 
     | 
    
         
            +
                      if using_poetry?
         
     | 
| 
      
 40 
     | 
    
         
            +
                        poetry_dependencies
         
     | 
| 
      
 41 
     | 
    
         
            +
                      else
         
     | 
| 
      
 42 
     | 
    
         
            +
                        pep621_dependencies
         
     | 
| 
      
 43 
     | 
    
         
            +
                      end
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    def poetry_dependencies
         
     | 
| 
       39 
47 
     | 
    
         
             
                      dependencies = Dependabot::FileParsers::Base::DependencySet.new
         
     | 
| 
       40 
48 
     | 
    
         | 
| 
       41 
49 
     | 
    
         
             
                      POETRY_DEPENDENCY_TYPES.each do |type|
         
     | 
| 
         @@ -59,6 +67,44 @@ module Dependabot 
     | 
|
| 
       59 
67 
     | 
    
         
             
                      dependencies
         
     | 
| 
       60 
68 
     | 
    
         
             
                    end
         
     | 
| 
       61 
69 
     | 
    
         | 
| 
      
 70 
     | 
    
         
            +
                    def pep621_dependencies
         
     | 
| 
      
 71 
     | 
    
         
            +
                      dependencies = Dependabot::FileParsers::Base::DependencySet.new
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                      # PDM is not yet supported, so we want to ignore it for now because in
         
     | 
| 
      
 74 
     | 
    
         
            +
                      # the current state of things, going on would result in updating
         
     | 
| 
      
 75 
     | 
    
         
            +
                      # pyproject.toml but leaving pdm.lock out of sync, which is
         
     | 
| 
      
 76 
     | 
    
         
            +
                      # undesirable. Leave PDM alone until properly supported
         
     | 
| 
      
 77 
     | 
    
         
            +
                      return dependencies if using_pdm?
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                      parsed_pep621_dependencies.each do |dep|
         
     | 
| 
      
 80 
     | 
    
         
            +
                        # If a requirement has a `<` or `<=` marker then updating it is
         
     | 
| 
      
 81 
     | 
    
         
            +
                        # probably blocked. Ignore it.
         
     | 
| 
      
 82 
     | 
    
         
            +
                        next if dep["markers"].include?("<")
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                        # If no requirement, don't add it
         
     | 
| 
      
 85 
     | 
    
         
            +
                        next if dep["requirement"].empty?
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                        dependencies <<
         
     | 
| 
      
 88 
     | 
    
         
            +
                          Dependency.new(
         
     | 
| 
      
 89 
     | 
    
         
            +
                            name: normalised_name(dep["name"], dep["extras"]),
         
     | 
| 
      
 90 
     | 
    
         
            +
                            version: dep["version"]&.include?("*") ? nil : dep["version"],
         
     | 
| 
      
 91 
     | 
    
         
            +
                            requirements: [{
         
     | 
| 
      
 92 
     | 
    
         
            +
                              requirement: dep["requirement"],
         
     | 
| 
      
 93 
     | 
    
         
            +
                              file: Pathname.new(dep["file"]).cleanpath.to_path,
         
     | 
| 
      
 94 
     | 
    
         
            +
                              source: nil,
         
     | 
| 
      
 95 
     | 
    
         
            +
                              groups: [dep["requirement_type"]]
         
     | 
| 
      
 96 
     | 
    
         
            +
                            }],
         
     | 
| 
      
 97 
     | 
    
         
            +
                            package_manager: "pip"
         
     | 
| 
      
 98 
     | 
    
         
            +
                          )
         
     | 
| 
      
 99 
     | 
    
         
            +
                      end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                      dependencies
         
     | 
| 
      
 102 
     | 
    
         
            +
                    end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    def normalised_name(name, extras)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      NameNormaliser.normalise_including_extras(name, extras)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
       62 
108 
     | 
    
         
             
                    # @param req can be an Array, Hash or String that represents the constraints for a dependency
         
     | 
| 
       63 
109 
     | 
    
         
             
                    def parse_requirements_from(req, type)
         
     | 
| 
       64 
110 
     | 
    
         
             
                      [req].flatten.compact.filter_map do |requirement|
         
     | 
| 
         @@ -75,6 +121,19 @@ module Dependabot 
     | 
|
| 
       75 
121 
     | 
    
         
             
                      end
         
     | 
| 
       76 
122 
     | 
    
         
             
                    end
         
     | 
| 
       77 
123 
     | 
    
         | 
| 
      
 124 
     | 
    
         
            +
                    def using_poetry?
         
     | 
| 
      
 125 
     | 
    
         
            +
                      !parsed_pyproject.dig("tool", "poetry").nil?
         
     | 
| 
      
 126 
     | 
    
         
            +
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    def using_pep621?
         
     | 
| 
      
 129 
     | 
    
         
            +
                      !parsed_pyproject.dig("project", "dependencies").nil? ||
         
     | 
| 
      
 130 
     | 
    
         
            +
                        !parsed_pyproject.dig("project", "optional-dependencies").nil?
         
     | 
| 
      
 131 
     | 
    
         
            +
                    end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                    def using_pdm?
         
     | 
| 
      
 134 
     | 
    
         
            +
                      using_pep621? && pdm_lock
         
     | 
| 
      
 135 
     | 
    
         
            +
                    end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
       78 
137 
     | 
    
         
             
                    # Create a DependencySet where each element has no requirement. Any
         
     | 
| 
       79 
138 
     | 
    
         
             
                    # requirements will be added when combining the DependencySet with
         
     | 
| 
       80 
139 
     | 
    
         
             
                    # other DependencySets.
         
     | 
| 
         @@ -146,6 +205,24 @@ module Dependabot 
     | 
|
| 
       146 
205 
     | 
    
         
             
                      poetry_lock || pyproject_lock
         
     | 
| 
       147 
206 
     | 
    
         
             
                    end
         
     | 
| 
       148 
207 
     | 
    
         | 
| 
      
 208 
     | 
    
         
            +
                    def parsed_pep621_dependencies
         
     | 
| 
      
 209 
     | 
    
         
            +
                      SharedHelpers.in_a_temporary_directory do
         
     | 
| 
      
 210 
     | 
    
         
            +
                        write_temporary_pyproject
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                        SharedHelpers.run_helper_subprocess(
         
     | 
| 
      
 213 
     | 
    
         
            +
                          command: "pyenv exec python #{NativeHelpers.python_helper_path}",
         
     | 
| 
      
 214 
     | 
    
         
            +
                          function: "parse_pep621_dependencies",
         
     | 
| 
      
 215 
     | 
    
         
            +
                          args: [pyproject.name]
         
     | 
| 
      
 216 
     | 
    
         
            +
                        )
         
     | 
| 
      
 217 
     | 
    
         
            +
                      end
         
     | 
| 
      
 218 
     | 
    
         
            +
                    end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                    def write_temporary_pyproject
         
     | 
| 
      
 221 
     | 
    
         
            +
                      path = pyproject.name
         
     | 
| 
      
 222 
     | 
    
         
            +
                      FileUtils.mkdir_p(Pathname.new(path).dirname)
         
     | 
| 
      
 223 
     | 
    
         
            +
                      File.write(path, pyproject.content)
         
     | 
| 
      
 224 
     | 
    
         
            +
                    end
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
       149 
226 
     | 
    
         
             
                    def parsed_lockfile
         
     | 
| 
       150 
227 
     | 
    
         
             
                      return parsed_poetry_lock if poetry_lock
         
     | 
| 
       151 
228 
     | 
    
         
             
                      return parsed_pyproject_lock if pyproject_lock
         
     | 
| 
         @@ -160,6 +237,11 @@ module Dependabot 
     | 
|
| 
       160 
237 
     | 
    
         
             
                      @poetry_lock ||=
         
     | 
| 
       161 
238 
     | 
    
         
             
                        dependency_files.find { |f| f.name == "poetry.lock" }
         
     | 
| 
       162 
239 
     | 
    
         
             
                    end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                    def pdm_lock
         
     | 
| 
      
 242 
     | 
    
         
            +
                      @pdm_lock ||=
         
     | 
| 
      
 243 
     | 
    
         
            +
                        dependency_files.find { |f| f.name == "pdm.lock" }
         
     | 
| 
      
 244 
     | 
    
         
            +
                    end
         
     | 
| 
       163 
245 
     | 
    
         
             
                  end
         
     | 
| 
       164 
246 
     | 
    
         
             
                end
         
     | 
| 
       165 
247 
     | 
    
         
             
              end
         
     | 
| 
         @@ -12,10 +12,10 @@ module Dependabot 
     | 
|
| 
       12 
12 
     | 
    
         
             
              module Python
         
     | 
| 
       13 
13 
     | 
    
         
             
                class FileParser
         
     | 
| 
       14 
14 
     | 
    
         
             
                  class SetupFileParser
         
     | 
| 
       15 
     | 
    
         
            -
                    INSTALL_REQUIRES_REGEX = /install_requires\s*=\s*\[/m 
     | 
| 
       16 
     | 
    
         
            -
                    SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*\[/m 
     | 
| 
       17 
     | 
    
         
            -
                    TESTS_REQUIRE_REGEX = /tests_require\s*=\s*\[/m 
     | 
| 
       18 
     | 
    
         
            -
                    EXTRAS_REQUIRE_REGEX = /extras_require\s*=\s*\{/m 
     | 
| 
      
 15 
     | 
    
         
            +
                    INSTALL_REQUIRES_REGEX = /install_requires\s*=\s*\[/m
         
     | 
| 
      
 16 
     | 
    
         
            +
                    SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*\[/m
         
     | 
| 
      
 17 
     | 
    
         
            +
                    TESTS_REQUIRE_REGEX = /tests_require\s*=\s*\[/m
         
     | 
| 
      
 18 
     | 
    
         
            +
                    EXTRAS_REQUIRE_REGEX = /extras_require\s*=\s*\{/m
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                    CLOSING_BRACKET = { "[" => "]", "{" => "}" }.freeze
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
         @@ -1,6 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            require "toml-rb"
         
     | 
| 
       4 
3 
     | 
    
         
             
            require "dependabot/dependency"
         
     | 
| 
       5 
4 
     | 
    
         
             
            require "dependabot/file_parsers"
         
     | 
| 
       6 
5 
     | 
    
         
             
            require "dependabot/file_parsers/base"
         
     | 
| 
         @@ -15,11 +14,9 @@ module Dependabot 
     | 
|
| 
       15 
14 
     | 
    
         
             
              module Python
         
     | 
| 
       16 
15 
     | 
    
         
             
                class FileParser < Dependabot::FileParsers::Base
         
     | 
| 
       17 
16 
     | 
    
         
             
                  require_relative "file_parser/pipfile_files_parser"
         
     | 
| 
       18 
     | 
    
         
            -
                  require_relative "file_parser/ 
     | 
| 
      
 17 
     | 
    
         
            +
                  require_relative "file_parser/pyproject_files_parser"
         
     | 
| 
       19 
18 
     | 
    
         
             
                  require_relative "file_parser/setup_file_parser"
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                  POETRY_DEPENDENCY_TYPES =
         
     | 
| 
       22 
     | 
    
         
            -
                    %w(tool.poetry.dependencies tool.poetry.dev-dependencies).freeze
         
     | 
| 
       23 
20 
     | 
    
         
             
                  DEPENDENCY_GROUP_KEYS = [
         
     | 
| 
       24 
21 
     | 
    
         
             
                    {
         
     | 
| 
       25 
22 
     | 
    
         
             
                      pipfile: "packages",
         
     | 
| 
         @@ -42,7 +39,7 @@ module Dependabot 
     | 
|
| 
       42 
39 
     | 
    
         
             
                    dependency_set = DependencySet.new
         
     | 
| 
       43 
40 
     | 
    
         | 
| 
       44 
41 
     | 
    
         
             
                    dependency_set += pipenv_dependencies if pipfile
         
     | 
| 
       45 
     | 
    
         
            -
                    dependency_set +=  
     | 
| 
      
 42 
     | 
    
         
            +
                    dependency_set += pyproject_file_dependencies if pyproject
         
     | 
| 
       46 
43 
     | 
    
         
             
                    dependency_set += requirement_dependencies if requirement_files.any?
         
     | 
| 
       47 
44 
     | 
    
         
             
                    dependency_set += setup_file_dependencies if setup_file || setup_cfg_file
         
     | 
| 
       48 
45 
     | 
    
         | 
| 
         @@ -62,9 +59,9 @@ module Dependabot 
     | 
|
| 
       62 
59 
     | 
    
         
             
                      dependency_set
         
     | 
| 
       63 
60 
     | 
    
         
             
                  end
         
     | 
| 
       64 
61 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
                  def  
     | 
| 
       66 
     | 
    
         
            -
                    @ 
     | 
| 
       67 
     | 
    
         
            -
                       
     | 
| 
      
 62 
     | 
    
         
            +
                  def pyproject_file_dependencies
         
     | 
| 
      
 63 
     | 
    
         
            +
                    @pyproject_file_dependencies ||=
         
     | 
| 
      
 64 
     | 
    
         
            +
                      PyprojectFilesParser.
         
     | 
| 
       68 
65 
     | 
    
         
             
                      new(dependency_files: dependency_files).
         
     | 
| 
       69 
66 
     | 
    
         
             
                      dependency_set
         
     | 
| 
       70 
67 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -105,18 +102,6 @@ module Dependabot 
     | 
|
| 
       105 
102 
     | 
    
         
             
                    end
         
     | 
| 
       106 
103 
     | 
    
         
             
                  end
         
     | 
| 
       107 
104 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                  def included_in_pipenv_deps?(dep_name)
         
     | 
| 
       109 
     | 
    
         
            -
                    return false unless pipfile
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                    pipenv_dependencies.dependencies.map(&:name).include?(dep_name)
         
     | 
| 
       112 
     | 
    
         
            -
                  end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                  def included_in_poetry_deps?(dep_name)
         
     | 
| 
       115 
     | 
    
         
            -
                    return false unless using_poetry?
         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
                    poetry_dependencies.dependencies.map(&:name).include?(dep_name)
         
     | 
| 
       118 
     | 
    
         
            -
                  end
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
105 
     | 
    
         
             
                  def blocking_marker?(dep)
         
     | 
| 
       121 
106 
     | 
    
         
             
                    return false if dep["markers"] == "None"
         
     | 
| 
       122 
107 
     | 
    
         
             
                    return true if dep["markers"].include?("<")
         
     | 
| 
         @@ -215,15 +200,6 @@ module Dependabot 
     | 
|
| 
       215 
200 
     | 
    
         
             
                    @pipfile_lock ||= get_original_file("Pipfile.lock")
         
     | 
| 
       216 
201 
     | 
    
         
             
                  end
         
     | 
| 
       217 
202 
     | 
    
         | 
| 
       218 
     | 
    
         
            -
                  def using_poetry?
         
     | 
| 
       219 
     | 
    
         
            -
                    return false unless pyproject
         
     | 
| 
       220 
     | 
    
         
            -
                    return true if poetry_lock || pyproject_lock
         
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
                    !TomlRB.parse(pyproject.content).dig("tool", "poetry").nil?
         
     | 
| 
       223 
     | 
    
         
            -
                  rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
         
     | 
| 
       224 
     | 
    
         
            -
                    raise Dependabot::DependencyFileNotParseable, pyproject.path
         
     | 
| 
       225 
     | 
    
         
            -
                  end
         
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
203 
     | 
    
         
             
                  def output_file_regex(filename)
         
     | 
| 
       228 
204 
     | 
    
         
             
                    "--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)"
         
     | 
| 
       229 
205 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -7,6 +7,7 @@ require "dependabot/python/file_fetcher" 
     | 
|
| 
       7 
7 
     | 
    
         
             
            require "dependabot/python/file_parser/python_requirement_parser"
         
     | 
| 
       8 
8 
     | 
    
         
             
            require "dependabot/python/file_updater"
         
     | 
| 
       9 
9 
     | 
    
         
             
            require "dependabot/shared_helpers"
         
     | 
| 
      
 10 
     | 
    
         
            +
            require "dependabot/python/helpers"
         
     | 
| 
       10 
11 
     | 
    
         
             
            require "dependabot/python/native_helpers"
         
     | 
| 
       11 
12 
     | 
    
         
             
            require "dependabot/python/python_versions"
         
     | 
| 
       12 
13 
     | 
    
         
             
            require "dependabot/python/name_normaliser"
         
     | 
| 
         @@ -22,10 +23,9 @@ module Dependabot 
     | 
|
| 
       22 
23 
     | 
    
         
             
                    require_relative "setup_file_sanitizer"
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
       24 
25 
     | 
    
         
             
                    UNSAFE_PACKAGES = %w(setuptools distribute pip).freeze
         
     | 
| 
       25 
     | 
    
         
            -
                    INCOMPATIBLE_VERSIONS_REGEX = /There are incompatible versions in the resolved dependencies:.*\z/m 
     | 
| 
       26 
     | 
    
         
            -
                    WARNINGS = /\s*# WARNING:.*\Z/m 
     | 
| 
       27 
     | 
    
         
            -
                    UNSAFE_NOTE =
         
     | 
| 
       28 
     | 
    
         
            -
                      /\s*# The following packages are considered to be unsafe.*\Z/m.freeze
         
     | 
| 
      
 26 
     | 
    
         
            +
                    INCOMPATIBLE_VERSIONS_REGEX = /There are incompatible versions in the resolved dependencies:.*\z/m
         
     | 
| 
      
 27 
     | 
    
         
            +
                    WARNINGS = /\s*# WARNING:.*\Z/m
         
     | 
| 
      
 28 
     | 
    
         
            +
                    UNSAFE_NOTE = /\s*# The following packages are considered to be unsafe.*\Z/m
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                    attr_reader :dependencies, :dependency_files, :credentials
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
         @@ -66,7 +66,7 @@ module Dependabot 
     | 
|
| 
       66 
66 
     | 
    
         
             
                    def compile_new_requirement_files
         
     | 
| 
       67 
67 
     | 
    
         
             
                      SharedHelpers.in_a_temporary_directory do
         
     | 
| 
       68 
68 
     | 
    
         
             
                        write_updated_dependency_files
         
     | 
| 
       69 
     | 
    
         
            -
                        install_required_python
         
     | 
| 
      
 69 
     | 
    
         
            +
                        Helpers.install_required_python(python_version)
         
     | 
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
                        filenames_to_compile.each do |filename|
         
     | 
| 
       72 
72 
     | 
    
         
             
                          # Shell out to pip-compile, generate a new set of requirements.
         
     | 
| 
         @@ -81,12 +81,6 @@ module Dependabot 
     | 
|
| 
       81 
81 
     | 
    
         
             
                            "#{SharedHelpers.escape_command(version_part)}",
         
     | 
| 
       82 
82 
     | 
    
         
             
                            allow_unsafe_shell_command: true
         
     | 
| 
       83 
83 
     | 
    
         
             
                          )
         
     | 
| 
       84 
     | 
    
         
            -
                          # Run pip-compile a second time, without an update argument, to
         
     | 
| 
       85 
     | 
    
         
            -
                          # ensure it resets the right comments.
         
     | 
| 
       86 
     | 
    
         
            -
                          run_pip_compile_command(
         
     | 
| 
       87 
     | 
    
         
            -
                            "pyenv exec pip-compile #{pip_compile_options(filename)} " \
         
     | 
| 
       88 
     | 
    
         
            -
                            "#{filename}"
         
     | 
| 
       89 
     | 
    
         
            -
                          )
         
     | 
| 
       90 
84 
     | 
    
         
             
                        end
         
     | 
| 
       91 
85 
     | 
    
         | 
| 
       92 
86 
     | 
    
         
             
                        # Remove any .python-version file before parsing the reqs
         
     | 
| 
         @@ -174,7 +168,7 @@ module Dependabot 
     | 
|
| 
       174 
168 
     | 
    
         
             
                    end
         
     | 
| 
       175 
169 
     | 
    
         | 
| 
       176 
170 
     | 
    
         
             
                    def run_pip_compile_command(command, allow_unsafe_shell_command: false)
         
     | 
| 
       177 
     | 
    
         
            -
                      run_command("pyenv local #{python_version}")
         
     | 
| 
      
 171 
     | 
    
         
            +
                      run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
         
     | 
| 
       178 
172 
     | 
    
         
             
                      run_command(
         
     | 
| 
       179 
173 
     | 
    
         
             
                        command,
         
     | 
| 
       180 
174 
     | 
    
         
             
                        allow_unsafe_shell_command: allow_unsafe_shell_command
         
     | 
| 
         @@ -204,7 +198,7 @@ module Dependabot 
     | 
|
| 
       204 
198 
     | 
    
         
             
                      end
         
     | 
| 
       205 
199 
     | 
    
         | 
| 
       206 
200 
     | 
    
         
             
                      # Overwrite the .python-version with updated content
         
     | 
| 
       207 
     | 
    
         
            -
                      File.write(".python-version", python_version)
         
     | 
| 
      
 201 
     | 
    
         
            +
                      File.write(".python-version", Helpers.python_major_minor(python_version))
         
     | 
| 
       208 
202 
     | 
    
         | 
| 
       209 
203 
     | 
    
         
             
                      setup_files.each do |file|
         
     | 
| 
       210 
204 
     | 
    
         
             
                        path = file.name
         
     | 
| 
         @@ -219,15 +213,6 @@ module Dependabot 
     | 
|
| 
       219 
213 
     | 
    
         
             
                      end
         
     | 
| 
       220 
214 
     | 
    
         
             
                    end
         
     | 
| 
       221 
215 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
                    def install_required_python
         
     | 
| 
       223 
     | 
    
         
            -
                      return if run_command("pyenv versions").include?("#{python_version}\n")
         
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
                      run_command("pyenv install -s #{python_version}")
         
     | 
| 
       226 
     | 
    
         
            -
                      run_command("pyenv exec pip install --upgrade pip")
         
     | 
| 
       227 
     | 
    
         
            -
                      run_command("pyenv exec pip install -r " \
         
     | 
| 
       228 
     | 
    
         
            -
                                  "#{NativeHelpers.python_requirements_path}")
         
     | 
| 
       229 
     | 
    
         
            -
                    end
         
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
216 
     | 
    
         
             
                    def sanitized_setup_file_content(file)
         
     | 
| 
       232 
217 
     | 
    
         
             
                      @sanitized_setup_file_content ||= {}
         
     | 
| 
       233 
218 
     | 
    
         
             
                      return @sanitized_setup_file_content[file.name] if @sanitized_setup_file_content[file.name]
         
     | 
| 
         @@ -133,6 +133,7 @@ module Dependabot 
     | 
|
| 
       133 
133 
     | 
    
         
             
                      content = freeze_other_dependencies(content)
         
     | 
| 
       134 
134 
     | 
    
         
             
                      content = freeze_dependencies_being_updated(content)
         
     | 
| 
       135 
135 
     | 
    
         
             
                      content = add_private_sources(content)
         
     | 
| 
      
 136 
     | 
    
         
            +
                      content = update_python_requirement(content)
         
     | 
| 
       136 
137 
     | 
    
         
             
                      content
         
     | 
| 
       137 
138 
     | 
    
         
             
                    end
         
     | 
| 
       138 
139 
     | 
    
         | 
| 
         @@ -142,6 +143,12 @@ module Dependabot 
     | 
|
| 
       142 
143 
     | 
    
         
             
                        freeze_top_level_dependencies_except(dependencies)
         
     | 
| 
       143 
144 
     | 
    
         
             
                    end
         
     | 
| 
       144 
145 
     | 
    
         | 
| 
      
 146 
     | 
    
         
            +
                    def update_python_requirement(pipfile_content)
         
     | 
| 
      
 147 
     | 
    
         
            +
                      PipfilePreparer.
         
     | 
| 
      
 148 
     | 
    
         
            +
                        new(pipfile_content: pipfile_content).
         
     | 
| 
      
 149 
     | 
    
         
            +
                        update_python_requirement(Helpers.python_major_minor(python_version))
         
     | 
| 
      
 150 
     | 
    
         
            +
                    end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
       145 
152 
     | 
    
         
             
                    # rubocop:disable Metrics/PerceivedComplexity
         
     | 
| 
       146 
153 
     | 
    
         
             
                    def freeze_dependencies_being_updated(pipfile_content)
         
     | 
| 
       147 
154 
     | 
    
         
             
                      pipfile_object = TomlRB.parse(pipfile_content)
         
     | 
| 
         @@ -246,7 +253,7 @@ module Dependabot 
     | 
|
| 
       246 
253 
     | 
    
         
             
                    def run_command(command, env: {})
         
     | 
| 
       247 
254 
     | 
    
         
             
                      start = Time.now
         
     | 
| 
       248 
255 
     | 
    
         
             
                      command = SharedHelpers.escape_command(command)
         
     | 
| 
       249 
     | 
    
         
            -
                      stdout, process = Open3. 
     | 
| 
      
 256 
     | 
    
         
            +
                      stdout, _, process = Open3.capture3(env, command)
         
     | 
| 
       250 
257 
     | 
    
         
             
                      time_taken = Time.now - start
         
     | 
| 
       251 
258 
     | 
    
         | 
| 
       252 
259 
     | 
    
         
             
                      # Raise an error with the output from the shell session if Pipenv
         
     | 
| 
         @@ -264,7 +271,7 @@ module Dependabot 
     | 
|
| 
       264 
271 
     | 
    
         
             
                    end
         
     | 
| 
       265 
272 
     | 
    
         | 
| 
       266 
273 
     | 
    
         
             
                    def run_pipenv_command(command, env: pipenv_env_variables)
         
     | 
| 
       267 
     | 
    
         
            -
                      run_command("pyenv local #{python_version}")
         
     | 
| 
      
 274 
     | 
    
         
            +
                      run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
         
     | 
| 
       268 
275 
     | 
    
         
             
                      run_command(command, env: env)
         
     | 
| 
       269 
276 
     | 
    
         
             
                    end
         
     | 
| 
       270 
277 
     | 
    
         | 
| 
         @@ -276,7 +283,7 @@ module Dependabot 
     | 
|
| 
       276 
283 
     | 
    
         
             
                      end
         
     | 
| 
       277 
284 
     | 
    
         | 
| 
       278 
285 
     | 
    
         
             
                      # Overwrite the .python-version with updated content
         
     | 
| 
       279 
     | 
    
         
            -
                      File.write(".python-version", python_version)
         
     | 
| 
      
 286 
     | 
    
         
            +
                      File.write(".python-version", Helpers.python_major_minor(python_version))
         
     | 
| 
       280 
287 
     | 
    
         | 
| 
       281 
288 
     | 
    
         
             
                      setup_files.each do |file|
         
     | 
| 
       282 
289 
     | 
    
         
             
                        path = file.name
         
     | 
| 
         @@ -302,11 +309,7 @@ module Dependabot 
     | 
|
| 
       302 
309 
     | 
    
         
             
                        nil
         
     | 
| 
       303 
310 
     | 
    
         
             
                      end
         
     | 
| 
       304 
311 
     | 
    
         | 
| 
       305 
     | 
    
         
            -
                       
     | 
| 
       306 
     | 
    
         
            -
             
     | 
| 
       307 
     | 
    
         
            -
                      requirements_path = NativeHelpers.python_requirements_path
         
     | 
| 
       308 
     | 
    
         
            -
                      run_command("pyenv install -s #{python_version}")
         
     | 
| 
       309 
     | 
    
         
            -
                      run_command("pyenv exec pip install -r #{requirements_path}")
         
     | 
| 
      
 312 
     | 
    
         
            +
                      Helpers.install_required_python(python_version)
         
     | 
| 
       310 
313 
     | 
    
         
             
                    end
         
     | 
| 
       311 
314 
     | 
    
         | 
| 
       312 
315 
     | 
    
         
             
                    def sanitized_setup_file_content(file)
         
     | 
| 
         @@ -70,10 +70,12 @@ module Dependabot 
     | 
|
| 
       70 
70 
     | 
    
         
             
                      pipfile_object = TomlRB.parse(pipfile_content)
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
       72 
72 
     | 
    
         
             
                      pipfile_object["requires"] ||= {}
         
     | 
| 
       73 
     | 
    
         
            -
                      pipfile_object 
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                      pipfile_object 
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
      
 73 
     | 
    
         
            +
                      if pipfile_object.dig("requires", "python_full_version") && pipfile_object.dig("requires", "python_version")
         
     | 
| 
      
 74 
     | 
    
         
            +
                        pipfile_object["requires"].delete("python_full_version")
         
     | 
| 
      
 75 
     | 
    
         
            +
                      elsif pipfile_object.dig("requires", "python_full_version")
         
     | 
| 
      
 76 
     | 
    
         
            +
                        pipfile_object["requires"].delete("python_full_version")
         
     | 
| 
      
 77 
     | 
    
         
            +
                        pipfile_object["requires"]["python_version"] = requirement
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
       77 
79 
     | 
    
         
             
                      TomlRB.dump(pipfile_object)
         
     | 
| 
       78 
80 
     | 
    
         
             
                    end
         
     | 
| 
       79 
81 
     | 
    
         | 
| 
         @@ -4,6 +4,7 @@ require "toml-rb" 
     | 
|
| 
       4 
4 
     | 
    
         
             
            require "open3"
         
     | 
| 
       5 
5 
     | 
    
         
             
            require "dependabot/dependency"
         
     | 
| 
       6 
6 
     | 
    
         
             
            require "dependabot/shared_helpers"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "dependabot/python/helpers"
         
     | 
| 
       7 
8 
     | 
    
         
             
            require "dependabot/python/version"
         
     | 
| 
       8 
9 
     | 
    
         
             
            require "dependabot/python/requirement"
         
     | 
| 
       9 
10 
     | 
    
         
             
            require "dependabot/python/python_versions"
         
     | 
| 
         @@ -105,6 +106,7 @@ module Dependabot 
     | 
|
| 
       105 
106 
     | 
    
         
             
                          content = sanitize(content)
         
     | 
| 
       106 
107 
     | 
    
         
             
                          content = freeze_other_dependencies(content)
         
     | 
| 
       107 
108 
     | 
    
         
             
                          content = freeze_dependencies_being_updated(content)
         
     | 
| 
      
 109 
     | 
    
         
            +
                          content = update_python_requirement(content)
         
     | 
| 
       108 
110 
     | 
    
         
             
                          content
         
     | 
| 
       109 
111 
     | 
    
         
             
                        end
         
     | 
| 
       110 
112 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -130,8 +132,14 @@ module Dependabot 
     | 
|
| 
       130 
132 
     | 
    
         
             
                      TomlRB.dump(pyproject_object)
         
     | 
| 
       131 
133 
     | 
    
         
             
                    end
         
     | 
| 
       132 
134 
     | 
    
         | 
| 
      
 135 
     | 
    
         
            +
                    def update_python_requirement(pyproject_content)
         
     | 
| 
      
 136 
     | 
    
         
            +
                      PyprojectPreparer.
         
     | 
| 
      
 137 
     | 
    
         
            +
                        new(pyproject_content: pyproject_content).
         
     | 
| 
      
 138 
     | 
    
         
            +
                        update_python_requirement(Helpers.python_major_minor(python_version))
         
     | 
| 
      
 139 
     | 
    
         
            +
                    end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
       133 
141 
     | 
    
         
             
                    def lock_declaration_to_new_version!(poetry_object, dep)
         
     | 
| 
       134 
     | 
    
         
            -
                      Dependabot::Python::FileParser:: 
     | 
| 
      
 142 
     | 
    
         
            +
                      Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
         
     | 
| 
       135 
143 
     | 
    
         
             
                        names = poetry_object[type]&.keys || []
         
     | 
| 
       136 
144 
     | 
    
         
             
                        pkg_name = names.find { |nm| normalise(nm) == dep.name }
         
     | 
| 
       137 
145 
     | 
    
         
             
                        next unless pkg_name
         
     | 
| 
         @@ -170,11 +178,11 @@ module Dependabot 
     | 
|
| 
       170 
178 
     | 
    
         
             
                          write_temporary_dependency_files(pyproject_content)
         
     | 
| 
       171 
179 
     | 
    
         
             
                          add_auth_env_vars
         
     | 
| 
       172 
180 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
                           
     | 
| 
       174 
     | 
    
         
            -
             
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
      
 181 
     | 
    
         
            +
                          Helpers.install_required_python(python_version)
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                          # use system git instead of the pure Python dulwich
         
     | 
| 
      
 184 
     | 
    
         
            +
                          unless python_version&.start_with?("3.6")
         
     | 
| 
      
 185 
     | 
    
         
            +
                            run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
         
     | 
| 
       178 
186 
     | 
    
         
             
                          end
         
     | 
| 
       179 
187 
     | 
    
         | 
| 
       180 
188 
     | 
    
         
             
                          run_poetry_command(poetry_update_command)
         
     | 
| 
         @@ -220,7 +228,7 @@ module Dependabot 
     | 
|
| 
       220 
228 
     | 
    
         
             
                      end
         
     | 
| 
       221 
229 
     | 
    
         | 
| 
       222 
230 
     | 
    
         
             
                      # Overwrite the .python-version with updated content
         
     | 
| 
       223 
     | 
    
         
            -
                      File.write(".python-version", python_version) if python_version
         
     | 
| 
      
 231 
     | 
    
         
            +
                      File.write(".python-version", Helpers.python_major_minor(python_version)) if python_version
         
     | 
| 
       224 
232 
     | 
    
         | 
| 
       225 
233 
     | 
    
         
             
                      # Overwrite the pyproject with updated content
         
     | 
| 
       226 
234 
     | 
    
         
             
                      File.write("pyproject.toml", pyproject_content)
         
     |