diff --git a/python/helpers/lib/parser.py b/python/helpers/lib/parser.py index b167318db4e..d4833d51b18 100644 --- a/python/helpers/lib/parser.py +++ b/python/helpers/lib/parser.py @@ -11,11 +11,62 @@ install_req_from_line, install_req_from_parsed_requirement, ) + +from packaging.requirements import InvalidRequirement, Requirement +import toml + # Inspired by pips internal check: # https://github.com/pypa/pip/blob/0bb3ac87f5bb149bd75cceac000844128b574385/src/pip/_internal/req/req_file.py#L35 COMMENT_RE = re.compile(r'(^|\s+)#.*$') +def parse_pep621_dependencies(pyproject_path): + DEPENDENCY_TYPES = ['dependencies', 'optional-dependencies'] + + def version_from_req(specifier_set): + if (len(specifier_set) == 1 and + next(iter(specifier_set)).operator in {"==", "==="}): + return next(iter(specifier_set)).version + + def flatten(foo): + for x in foo: + if hasattr(x, '__iter__') and not isinstance(x, str): + for y in flatten(x): + yield y + else: + yield x + + def parse_dependencies(dependency_type): + dependencies = [] + requirement_packages = [] + + try: + project = toml.load(pyproject_path)['project'] + if dependency_type in project: + dependencies = flatten(project[dependency_type]) + for dependency in dependencies: + req = Requirement(dependency) + requirement_packages.append({ + "name": req.name, + "version": version_from_req(req.specifier), + "markers": str(req.marker) or None, + "file": pyproject_path, + "requirement": str(req.specifier), + "requirement_type": dependency_type, + "extras": sorted(list(req.extras)) + }) + except KeyError as e: + print(json.dumps({"error": repr(e)})) + exit(1) + except InvalidRequirement as e: + print(json.dumps({"error": repr(e)})) + exit(1) + else: + return requirement_packages + + parsed_dependencies = list(map(parse_dependencies, DEPENDENCY_TYPES)) + return json.dumps({"result": parsed_dependencies}) + def parse_requirements(directory): # Parse the requirements.txt requirement_packages = [] diff --git a/python/helpers/run.py b/python/helpers/run.py index 26961e502d2..f80f8ff2ec5 100644 --- a/python/helpers/run.py +++ b/python/helpers/run.py @@ -10,6 +10,8 @@ print(parser.parse_requirements(args["args"][0])) elif args["function"] == "parse_setup": print(parser.parse_setup(args["args"][0])) + elif args["function"] == "parse_pep621_dependencies": + print(parser.parse_pep621_dependencies(args["args"][0])) elif args["function"] == "get_dependency_hash": print(hasher.get_dependency_hash(*args["args"])) elif args["function"] == "get_pipfile_hash": diff --git a/python/lib/dependabot/python/file_fetcher.rb b/python/lib/dependabot/python/file_fetcher.rb index 5a2aea67c47..1318c875d5a 100644 --- a/python/lib/dependabot/python/file_fetcher.rb +++ b/python/lib/dependabot/python/file_fetcher.rb @@ -5,7 +5,7 @@ require "dependabot/file_fetchers" require "dependabot/file_fetchers/base" require "dependabot/python/requirement_parser" -require "dependabot/python/file_parser/poetry_files_parser" +require "dependabot/python/file_parser/pyproject_files_parser" require "dependabot/errors" module Dependabot @@ -24,7 +24,7 @@ def self.required_files_in?(filenames) # If this repo is using a Pipfile return true return true if filenames.include?("Pipfile") - # If this repo is using Poetry return true + # If this repo is using pyproject.toml return true return true if filenames.include?("pyproject.toml") return true if filenames.include?("setup.py") @@ -296,8 +296,8 @@ def fetch_path_setup_file(path, allow_pyproject: false) fetch_submodules: true ).tap { |f| f.support_file = true } rescue Dependabot::DependencyFileNotFound - # For Poetry projects attempt to fetch a pyproject.toml at the - # given path instead of a setup.py. We do not require a + # For projects with pyproject.toml attempt to fetch a pyproject.toml + # at the given path instead of a setup.py. We do not require a # setup.py to be present, so if none can be found, simply return return [] unless allow_pyproject @@ -395,7 +395,7 @@ def poetry_path_setup_file_paths return [] unless pyproject paths = [] - Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |dep_type| + Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |dep_type| next unless parsed_pyproject.dig("tool", "poetry", dep_type) parsed_pyproject.dig("tool", "poetry", dep_type).each do |_, req| diff --git a/python/lib/dependabot/python/file_parser.rb b/python/lib/dependabot/python/file_parser.rb index 0aa6c1e750c..8ecc641763a 100644 --- a/python/lib/dependabot/python/file_parser.rb +++ b/python/lib/dependabot/python/file_parser.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "toml-rb" require "dependabot/dependency" require "dependabot/file_parsers" require "dependabot/file_parsers/base" @@ -15,11 +14,9 @@ module Dependabot module Python class FileParser < Dependabot::FileParsers::Base require_relative "file_parser/pipfile_files_parser" - require_relative "file_parser/poetry_files_parser" + require_relative "file_parser/pyproject_files_parser" require_relative "file_parser/setup_file_parser" - POETRY_DEPENDENCY_TYPES = - %w(tool.poetry.dependencies tool.poetry.dev-dependencies).freeze DEPENDENCY_GROUP_KEYS = [ { pipfile: "packages", @@ -42,7 +39,7 @@ def parse dependency_set = DependencySet.new dependency_set += pipenv_dependencies if pipfile - dependency_set += poetry_dependencies if using_poetry? + dependency_set += pyproject_file_dependencies if pyproject dependency_set += requirement_dependencies if requirement_files.any? dependency_set += setup_file_dependencies if setup_file || setup_cfg_file @@ -62,9 +59,9 @@ def pipenv_dependencies dependency_set end - def poetry_dependencies - @poetry_dependencies ||= - PoetryFilesParser. + def pyproject_file_dependencies + @pyproject_file_dependencies ||= + PyprojectFilesParser. new(dependency_files: dependency_files). dependency_set end @@ -105,18 +102,6 @@ def group_from_filename(filename) end end - def included_in_pipenv_deps?(dep_name) - return false unless pipfile - - pipenv_dependencies.dependencies.map(&:name).include?(dep_name) - end - - def included_in_poetry_deps?(dep_name) - return false unless using_poetry? - - poetry_dependencies.dependencies.map(&:name).include?(dep_name) - end - def blocking_marker?(dep) return false if dep["markers"] == "None" return true if dep["markers"].include?("<") @@ -215,15 +200,6 @@ def pipfile_lock @pipfile_lock ||= get_original_file("Pipfile.lock") end - def using_poetry? - return false unless pyproject - return true if poetry_lock || pyproject_lock - - !TomlRB.parse(pyproject.content).dig("tool", "poetry").nil? - rescue TomlRB::ParseError, TomlRB::ValueOverwriteError - raise Dependabot::DependencyFileNotParseable, pyproject.path - end - def output_file_regex(filename) "--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)" end diff --git a/python/lib/dependabot/python/file_parser/poetry_files_parser.rb b/python/lib/dependabot/python/file_parser/pyproject_files_parser.rb similarity index 70% rename from python/lib/dependabot/python/file_parser/poetry_files_parser.rb rename to python/lib/dependabot/python/file_parser/pyproject_files_parser.rb index f604718f8f6..fe91f52a63f 100644 --- a/python/lib/dependabot/python/file_parser/poetry_files_parser.rb +++ b/python/lib/dependabot/python/file_parser/pyproject_files_parser.rb @@ -12,7 +12,7 @@ module Dependabot module Python class FileParser - class PoetryFilesParser + class PyprojectFilesParser POETRY_DEPENDENCY_TYPES = %w(dependencies dev-dependencies).freeze # https://python-poetry.org/docs/dependency-specification/ @@ -25,7 +25,7 @@ def initialize(dependency_files:) def dependency_set dependency_set = Dependabot::FileParsers::Base::DependencySet.new - dependency_set += pyproject_dependencies + dependency_set += pyproject_dependencies if using_poetry? || using_pep621? dependency_set += lockfile_dependencies if lockfile dependency_set @@ -36,6 +36,14 @@ def dependency_set attr_reader :dependency_files def pyproject_dependencies + if using_poetry? + poetry_dependencies + else + pep621_dependencies + end + end + + def poetry_dependencies dependencies = Dependabot::FileParsers::Base::DependencySet.new POETRY_DEPENDENCY_TYPES.each do |type| @@ -59,6 +67,38 @@ def pyproject_dependencies dependencies end + def pep621_dependencies + dependencies = Dependabot::FileParsers::Base::DependencySet.new + + parsed_pep621_dependencies.each do |dep| + # If a requirement has a `<` or `<=` marker then updating it is + # probably blocked. Ignore it. + next if dep["markers"].include?("<") + + # If no requirement, don't add it + next if dep["requirement"].empty? + + dependencies << + Dependency.new( + name: normalised_name(dep["name"], dep["extras"]), + version: dep["version"]&.include?("*") ? nil : dep["version"], + requirements: [{ + requirement: dep["requirement"], + file: Pathname.new(dep["file"]).cleanpath.to_path, + source: nil, + groups: [dep["requirement_type"]] + }], + package_manager: "pip" + ) + end + + dependencies + end + + def normalised_name(name, extras) + NameNormaliser.normalise_including_extras(name, extras) + end + # @param req can be an Array, Hash or String that represents the constraints for a dependency def parse_requirements_from(req, type) [req].flatten.compact.filter_map do |requirement| @@ -75,6 +115,14 @@ def parse_requirements_from(req, type) end end + def using_poetry? + !parsed_pyproject.dig("tool", "poetry").nil? + end + + def using_pep621? + !parsed_pyproject.dig("project", "dependencies").nil? + end + # Create a DependencySet where each element has no requirement. Any # requirements will be added when combining the DependencySet with # other DependencySets. @@ -146,6 +194,24 @@ def lockfile poetry_lock || pyproject_lock end + def parsed_pep621_dependencies + SharedHelpers.in_a_temporary_directory do + write_temporary_pyproject + + SharedHelpers.run_helper_subprocess( + command: "pyenv exec python #{NativeHelpers.python_helper_path}", + function: "parse_pep621_dependencies", + args: [pyproject.name] + ) + end + end + + def write_temporary_pyproject + path = pyproject.name + FileUtils.mkdir_p(Pathname.new(path).dirname) + File.write(path, pyproject.content) + end + def parsed_lockfile return parsed_poetry_lock if poetry_lock return parsed_pyproject_lock if pyproject_lock diff --git a/python/lib/dependabot/python/file_updater.rb b/python/lib/dependabot/python/file_updater.rb index 75414ec0a96..94cea9f6070 100644 --- a/python/lib/dependabot/python/file_updater.rb +++ b/python/lib/dependabot/python/file_updater.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "toml-rb" require "dependabot/file_updaters" require "dependabot/file_updaters/base" require "dependabot/shared_helpers" @@ -61,7 +62,13 @@ def resolver_type # Otherwise, this is a top-level dependency, and we can figure out # which resolver to use based on the filename of its requirements return :pipfile if changed_req_files.any?("Pipfile") - return :poetry if changed_req_files.any?("pyproject.toml") + + if changed_req_files.any?("pyproject.toml") + return :poetry if poetry_based? + + return :requirements + end + return :pip_compile if changed_req_files.any? { |f| f.end_with?(".in") } :requirements @@ -119,6 +126,12 @@ def check_required_files raise "Missing required files!" end + def poetry_based? + return false unless pyproject + + !TomlRB.parse(pyproject.content).dig("tool", "poetry").nil? + end + def pipfile @pipfile ||= get_original_file("Pipfile") end diff --git a/python/lib/dependabot/python/file_updater/poetry_file_updater.rb b/python/lib/dependabot/python/file_updater/poetry_file_updater.rb index 9b53ac74e45..1fba530ea2f 100644 --- a/python/lib/dependabot/python/file_updater/poetry_file_updater.rb +++ b/python/lib/dependabot/python/file_updater/poetry_file_updater.rb @@ -131,7 +131,7 @@ def freeze_dependencies_being_updated(pyproject_content) end def lock_declaration_to_new_version!(poetry_object, dep) - Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |type| + Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type| names = poetry_object[type]&.keys || [] pkg_name = names.find { |nm| normalise(nm) == dep.name } next unless pkg_name diff --git a/python/lib/dependabot/python/file_updater/pyproject_preparer.rb b/python/lib/dependabot/python/file_updater/pyproject_preparer.rb index 72fd1649664..08fca9222b7 100644 --- a/python/lib/dependabot/python/file_updater/pyproject_preparer.rb +++ b/python/lib/dependabot/python/file_updater/pyproject_preparer.rb @@ -52,7 +52,7 @@ def freeze_top_level_dependencies_except(dependencies) poetry_object = pyproject_object["tool"]["poetry"] excluded_names = dependencies.map(&:name) + ["python"] - Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |key| + Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |key| next unless poetry_object[key] source_types = %w(directory file url) diff --git a/python/lib/dependabot/python/update_checker.rb b/python/lib/dependabot/python/update_checker.rb index ef673477d86..32ed70fab41 100644 --- a/python/lib/dependabot/python/update_checker.rb +++ b/python/lib/dependabot/python/update_checker.rb @@ -144,7 +144,9 @@ def resolver_type # Otherwise, this is a top-level dependency, and we can figure out # which resolver to use based on the filename of its requirements return :pipenv if req_files.any?("Pipfile") - return :poetry if req_files.any?("pyproject.toml") + + return pyproject_resolver if req_files.any?("pyproject.toml") + return :pip_compile if req_files.any? { |f| f.end_with?(".in") } if dependency.version && !exact_requirement?(reqs) @@ -162,6 +164,12 @@ def subdependency_resolver raise "Claimed to be a sub-dependency, but no lockfile exists!" end + def pyproject_resolver + return :poetry if poetry_based? + + :requirements + end + def exact_requirement?(reqs) reqs = reqs.map { |r| r.fetch(:requirement) } reqs = reqs.compact @@ -261,22 +269,23 @@ def latest_version_finder ) end + def poetry_based? + pyproject && !poetry_details.nil? + end + def poetry_library? - return false unless pyproject + return false unless poetry_based? # Hit PyPi and check whether there are details for a library with a # matching name and description - details = TomlRB.parse(pyproject.content).dig("tool", "poetry") - return false unless details - index_response = Dependabot::RegistryClient.get( - url: "https://pypi.org/pypi/#{normalised_name(details['name'])}/json/" + url: "https://pypi.org/pypi/#{normalised_name(poetry_details['name'])}/json/" ) return false unless index_response.status == 200 pypi_info = JSON.parse(index_response.body)["info"] || {} - pypi_info["summary"] == details["description"] + pypi_info["summary"] == poetry_details["description"] rescue URI::InvalidURIError false end @@ -305,6 +314,10 @@ def poetry_lock dependency_files.find { |f| f.name == "poetry.lock" } end + def poetry_details + @poetry_details ||= TomlRB.parse(pyproject.content).dig("tool", "poetry") + end + def pip_compile_files dependency_files.select { |f| f.name.end_with?(".in") } end diff --git a/python/lib/dependabot/python/update_checker/poetry_version_resolver.rb b/python/lib/dependabot/python/update_checker/poetry_version_resolver.rb index acd20b774d5..f4343fdade8 100644 --- a/python/lib/dependabot/python/update_checker/poetry_version_resolver.rb +++ b/python/lib/dependabot/python/update_checker/poetry_version_resolver.rb @@ -293,7 +293,7 @@ def set_target_dependency_req(pyproject_content, updated_requirement) pyproject_object = TomlRB.parse(pyproject_content) poetry_object = pyproject_object.dig("tool", "poetry") - Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |type| + Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type| names = poetry_object[type]&.keys || [] pkg_name = names.find { |nm| normalise(nm) == dependency.name } next unless pkg_name diff --git a/python/spec/dependabot/python/file_parser/poetry_files_parser_spec.rb b/python/spec/dependabot/python/file_parser/pyproject_files_parser_spec.rb similarity index 79% rename from python/spec/dependabot/python/file_parser/poetry_files_parser_spec.rb rename to python/spec/dependabot/python/file_parser/pyproject_files_parser_spec.rb index b0d3bf6ee7e..014ee07307c 100644 --- a/python/spec/dependabot/python/file_parser/poetry_files_parser_spec.rb +++ b/python/spec/dependabot/python/file_parser/pyproject_files_parser_spec.rb @@ -2,9 +2,9 @@ require "spec_helper" require "dependabot/dependency_file" -require "dependabot/python/file_parser/poetry_files_parser" +require "dependabot/python/file_parser/pyproject_files_parser" -RSpec.describe Dependabot::Python::FileParser::PoetryFilesParser do +RSpec.describe Dependabot::Python::FileParser::PyprojectFilesParser do let(:parser) { described_class.new(dependency_files: files) } let(:files) { [pyproject] } @@ -17,9 +17,10 @@ let(:pyproject_body) do fixture("pyproject_files", pyproject_fixture_name) end - let(:pyproject_fixture_name) { "pyproject.toml" } - describe "parse" do + describe "parse poetry files" do + let(:pyproject_fixture_name) { "poetry.toml" } + subject(:dependencies) { parser.dependency_set.dependencies } context "without a lockfile" do @@ -102,17 +103,17 @@ end context "with a lockfile" do - let(:files) { [pyproject, pyproject_lock] } - let(:pyproject_lock) do + let(:files) { [pyproject, poetry_lock] } + let(:poetry_lock) do Dependabot::DependencyFile.new( - name: "pyproject.lock", + name: "poetry.lock", content: pyproject_lock_body ) end let(:pyproject_lock_body) do fixture("pyproject_locks", pyproject_lock_fixture_name) end - let(:pyproject_lock_fixture_name) { "pyproject.lock" } + let(:pyproject_lock_fixture_name) { "poetry.lock" } its(:length) { is_expected.to eq(36) } @@ -120,11 +121,11 @@ expect(dependencies.map(&:name)).to_not include("python") end - context "that is called poetry.lock" do - let(:files) { [pyproject, poetry_lock] } - let(:poetry_lock) do + context "that is called pyproject.lock (legacy name)" do + let(:files) { [pyproject, pyproject_lock] } + let(:pyproject_lock) do Dependabot::DependencyFile.new( - name: "poetry.lock", + name: "pyproject.lock", content: pyproject_lock_body ) end @@ -228,4 +229,46 @@ end end end + + describe "parse standard python files" do + let(:pyproject_fixture_name) { "standard_python.toml" } + + subject(:dependencies) { parser.dependency_set.dependencies } + + its(:length) { is_expected.to eq(1) } + + context "with a string declaration" do + subject(:dependency) { dependencies.first } + + it "has the right details" do + expect(dependency).to be_a(Dependabot::Dependency) + expect(dependency.name).to eq("ansys-templates") + expect(dependency.version).to eq("0.3.0") + expect(dependency.requirements).to eq( + [{ + requirement: "==0.3.0", + file: "pyproject.toml", + groups: [nil], + source: nil + }] + ) + end + end + + context "without dependencies" do + let(:pyproject_fixture_name) { "no_dependencies.toml" } + + subject(:dependencies) { parser.dependency_set.dependencies } + + its(:length) { is_expected.to eq(0) } + end + + context "with dependencies with empty requirements" do + let(:pyproject_fixture_name) { "no_requirements.toml" } + + subject(:dependencies) { parser.dependency_set.dependencies } + + its(:length) { is_expected.to eq(0) } + end + end end diff --git a/python/spec/dependabot/python/file_parser_spec.rb b/python/spec/dependabot/python/file_parser_spec.rb index fb944a54dd5..cfd97c477b2 100644 --- a/python/spec/dependabot/python/file_parser_spec.rb +++ b/python/spec/dependabot/python/file_parser_spec.rb @@ -1212,18 +1212,18 @@ end end - context "with a pyproject.toml and pyproject.lock" do + context "with a pyproject.toml in poetry format and pyproject.lock legacy poetry lock file" do let(:files) { [pyproject, pyproject_lock] } let(:pyproject) do Dependabot::DependencyFile.new( name: "pyproject.toml", - content: fixture("pyproject_files", "pyproject.toml") + content: fixture("pyproject_files", "poetry.toml") ) end let(:pyproject_lock) do Dependabot::DependencyFile.new( name: "pyproject.lock", - content: fixture("pyproject_locks", "pyproject.lock") + content: fixture("pyproject_locks", "poetry.lock") ) end diff --git a/python/spec/dependabot/python/file_updater/pipfile_file_updater_spec.rb b/python/spec/dependabot/python/file_updater/pipfile_file_updater_spec.rb index fae5cc0b6ba..fd936a86543 100644 --- a/python/spec/dependabot/python/file_updater/pipfile_file_updater_spec.rb +++ b/python/spec/dependabot/python/file_updater/pipfile_file_updater_spec.rb @@ -155,7 +155,7 @@ let(:pyproject) do Dependabot::DependencyFile.new( name: "pyproject.toml", - content: fixture("pyproject_files", "pyproject.toml") + content: fixture("pyproject_files", "poetry.toml") ) end diff --git a/python/spec/dependabot/python/file_updater/pyproject_preparer_spec.rb b/python/spec/dependabot/python/file_updater/pyproject_preparer_spec.rb index f48fd41a648..5e665ded0b2 100644 --- a/python/spec/dependabot/python/file_updater/pyproject_preparer_spec.rb +++ b/python/spec/dependabot/python/file_updater/pyproject_preparer_spec.rb @@ -14,7 +14,7 @@ end let(:lockfile) { nil } let(:pyproject_content) { fixture("pyproject_files", pyproject_fixture_name) } - let(:pyproject_fixture_name) { "pyproject.toml" } + let(:pyproject_fixture_name) { "poetry.toml" } describe "#add_auth_env_vars" do it "adds auth env vars when a token is present" do @@ -48,7 +48,7 @@ it "doesn't break when there are no private sources" do preparer = Dependabot::Python::FileUpdater::PyprojectPreparer.new( - pyproject_content: fixture("pyproject_files", "pyproject.toml"), + pyproject_content: pyproject_content, lockfile: nil ) expect { preparer.add_auth_env_vars(nil) }.not_to raise_error @@ -99,7 +99,7 @@ let(:pyproject_lock_body) do fixture("pyproject_locks", pyproject_lock_fixture_name) end - let(:pyproject_lock_fixture_name) { "pyproject.lock" } + let(:pyproject_lock_fixture_name) { "poetry.lock" } context "with no dependencies to except" do let(:dependencies) { [] } diff --git a/python/spec/dependabot/python/update_checker/pipenv_version_resolver_spec.rb b/python/spec/dependabot/python/update_checker/pipenv_version_resolver_spec.rb index aec3147ccc5..dcdbaa4a76c 100644 --- a/python/spec/dependabot/python/update_checker/pipenv_version_resolver_spec.rb +++ b/python/spec/dependabot/python/update_checker/pipenv_version_resolver_spec.rb @@ -188,7 +188,7 @@ let(:pyproject) do Dependabot::DependencyFile.new( name: "pyproject.toml", - content: fixture("pyproject_files", "pyproject.toml") + content: fixture("pyproject_files", "poetry.toml") ) end diff --git a/python/spec/fixtures/pyproject_files/no_dependencies.toml b/python/spec/fixtures/pyproject_files/no_dependencies.toml new file mode 100644 index 00000000000..c6d60e1a4a2 --- /dev/null +++ b/python/spec/fixtures/pyproject_files/no_dependencies.toml @@ -0,0 +1,10 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "pkgtest" +authors = [{name = "Sample", email = "sample.project@example.org"}] +license = {file = "LICENSE"} +classifiers = ["License :: OSI Approved :: MIT License"] +dynamic = ["version", "description"] diff --git a/python/spec/fixtures/pyproject_files/no_requirements.toml b/python/spec/fixtures/pyproject_files/no_requirements.toml new file mode 100644 index 00000000000..48022376300 --- /dev/null +++ b/python/spec/fixtures/pyproject_files/no_requirements.toml @@ -0,0 +1,31 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "httpx" +description = "The next generation HTTP client." +license = "BSD-3-Clause" +requires-python = ">=3.7" +authors = [ + { name = "Tom Christie", email = "tom@tomchristie.com" }, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Web Environment", + "Framework :: AsyncIO", + "Framework :: Trio", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Topic :: Internet :: WWW/HTTP", +] +dependencies = [ + "certifi" +] diff --git a/python/spec/fixtures/pyproject_files/pyproject.toml b/python/spec/fixtures/pyproject_files/poetry.toml similarity index 100% rename from python/spec/fixtures/pyproject_files/pyproject.toml rename to python/spec/fixtures/pyproject_files/poetry.toml diff --git a/python/spec/fixtures/pyproject_files/standard_python.toml b/python/spec/fixtures/pyproject_files/standard_python.toml new file mode 100644 index 00000000000..a7c4bf33f69 --- /dev/null +++ b/python/spec/fixtures/pyproject_files/standard_python.toml @@ -0,0 +1,21 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "pkgtest" +authors = [{name = "Sample", email = "sample.project@example.org"}] +license = {file = "LICENSE"} +classifiers = ["License :: OSI Approved :: MIT License"] +dynamic = ["version", "description"] +dependencies = [ + "ansys-templates==0.3.0", +] + +[project.optional-dependencies] +socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ] +tests = [ + 'ddt >= 1.2.2, < 2', + 'pytest < 6', + 'mock >= 1.0.1, < 4; python_version < "3.4"', +] diff --git a/python/spec/fixtures/pyproject_locks/pyproject.lock b/python/spec/fixtures/pyproject_locks/poetry.lock similarity index 100% rename from python/spec/fixtures/pyproject_locks/pyproject.lock rename to python/spec/fixtures/pyproject_locks/poetry.lock