Skip to content

Commit

Permalink
Merge pull request #3423 from honnix/update-setup-cfg
Browse files Browse the repository at this point in the history
Update setup cfg
  • Loading branch information
feelepxyz authored Apr 19, 2021
2 parents a873380 + 9fd5b10 commit 2606bdc
Show file tree
Hide file tree
Showing 20 changed files with 1,215 additions and 165 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ vendor
**/bin/helper
/.core-bash_history
coverage/
.ruby-gemset
.ruby-version
117 changes: 77 additions & 40 deletions python/helpers/lib/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,38 +57,50 @@ def version_from_install_req(install_req):


def parse_setup(directory):
# Parse the setup.py
setup_packages = []
if os.path.isfile(directory + '/setup.py'):
def version_from_install_req(install_req):
if install_req.is_pinned:
return next(iter(install_req.specifier)).version
def version_from_install_req(install_req):
if install_req.is_pinned:
return next(iter(install_req.specifier)).version

def parse_requirement(req, req_type):
install_req = install_req_from_line(req)
if install_req.original_link:
return
def parse_requirement(req, req_type, filename):
install_req = install_req_from_line(req)
if install_req.original_link:
return

setup_packages.append({
setup_packages.append(
{
"name": install_req.req.name,
"version": version_from_install_req(install_req),
"markers": str(install_req.markers) or None,
"file": "setup.py",
"file": filename,
"requirement": str(install_req.specifier) or None,
"requirement_type": req_type,
"extras": sorted(list(install_req.extras))
})
"extras": sorted(list(install_req.extras)),
}
)

def parse_requirements(requires, req_type, filename):
for req in requires:
parse_requirement(req, req_type, filename)

# Parse the setup.py and setup.cfg
setup_py = "setup.py"
setup_py_path = os.path.join(directory, setup_py)
setup_cfg = "setup.cfg"
setup_cfg_path = os.path.join(directory, setup_cfg)
setup_packages = []

if os.path.isfile(setup_py_path):

def setup(*args, **kwargs):
for arg in ['setup_requires', 'install_requires', 'tests_require']:
if not kwargs.get(arg):
continue
for req in kwargs.get(arg):
parse_requirement(req, arg)
extras_require_dict = kwargs.get('extras_require', {})
for key in extras_require_dict:
for req in extras_require_dict[key]:
parse_requirement(req, 'extras_require:{}'.format(key))
for arg in ["setup_requires", "install_requires", "tests_require"]:
requires = kwargs.get(arg, [])
parse_requirements(requires, arg, setup_py)
extras_require_dict = kwargs.get("extras_require", {})
for key, value in extras_require_dict.items():
parse_requirements(
value, "extras_require:{}".format(key), setup_py
)

setuptools.setup = setup

def noop(*args, **kwargs):
Expand All @@ -100,17 +112,19 @@ def fake_parse(*args, **kwargs):
global fake_open

def fake_open(*args, **kwargs):
content = ("VERSION = ('0', '0', '1+dependabot')\n"
"__version__ = '0.0.1+dependabot'\n"
"__author__ = 'someone'\n"
"__title__ = 'something'\n"
"__description__ = 'something'\n"
"__author_email__ = 'something'\n"
"__license__ = 'something'\n"
"__url__ = 'something'\n")
content = (
"VERSION = ('0', '0', '1+dependabot')\n"
"__version__ = '0.0.1+dependabot'\n"
"__author__ = 'someone'\n"
"__title__ = 'something'\n"
"__description__ = 'something'\n"
"__author_email__ = 'something'\n"
"__license__ = 'something'\n"
"__url__ = 'something'\n"
)
return io.StringIO(content)

content = open(directory + '/setup.py', 'r').read()
content = open(setup_py_path, "r").read()

# Remove `print`, `open`, `log` and import statements
content = re.sub(r"print\s*\(", "noop(", content)
Expand All @@ -121,18 +135,41 @@ def fake_open(*args, **kwargs):
content = re.sub(version_re, "", content)

# Set variables likely to be imported
__version__ = '0.0.1+dependabot'
__author__ = 'someone'
__title__ = 'something'
__description__ = 'something'
__author_email__ = 'something'
__license__ = 'something'
__url__ = 'something'
__version__ = "0.0.1+dependabot"
__author__ = "someone"
__title__ = "something"
__description__ = "something"
__author_email__ = "something"
__license__ = "something"
__url__ = "something"

# Run as main (since setup.py is a script)
__name__ = '__main__'
__name__ = "__main__"

# Exec the setup.py
exec(content) in globals(), locals()

if os.path.isfile(setup_cfg_path):
try:
config = setuptools.config.read_configuration(setup_cfg_path)

for req_type in [
"setup_requires",
"install_requires",
"tests_require",
]:
requires = config.get("options", {}).get(req_type, [])
parse_requirements(requires, req_type, setup_cfg)

extras_require = config.get("options", {}).get(
"extras_require", {}
)
for key, value in extras_require.items():
parse_requirements(
value, "extras_require:{}".format(key), setup_cfg
)
except Exception as e:
print(json.dumps({"error": repr(e)}))
exit(1)

return json.dumps({"result": setup_packages})
2 changes: 1 addition & 1 deletion python/helpers/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

if args["function"] == "parse_requirements":
print(parser.parse_requirements(args["args"][0]))
if args["function"] == "parse_setup":
elif args["function"] == "parse_setup":
print(parser.parse_setup(args["args"][0]))
elif args["function"] == "get_dependency_hash":
print(hasher.get_dependency_hash(*args["args"]))
Expand Down
14 changes: 8 additions & 6 deletions python/lib/dependabot/python/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ def self.required_files_in?(filenames)
# If this repo is using Poetry return true
return true if filenames.include?("pyproject.toml")

filenames.include?("setup.py")
return true if filenames.include?("setup.py")

filenames.include?("setup.cfg")
end

def self.required_files_message
"Repo must contain a requirements.txt, setup.py, pyproject.toml, "\
"Repo must contain a requirements.txt, setup.py, setup.cfg, pyproject.toml, "\
"or a Pipfile."
end

Expand All @@ -45,7 +47,7 @@ def fetch_files
fetched_files += requirement_files if requirements_txt_files.any?

fetched_files << setup_file if setup_file
fetched_files << setup_cfg if setup_cfg
fetched_files << setup_cfg_file if setup_cfg_file
fetched_files += path_setup_files
fetched_files << pip_conf if pip_conf
fetched_files << python_version if python_version
Expand Down Expand Up @@ -77,7 +79,7 @@ def requirement_files
end

def check_required_files_present
return if requirements_txt_files.any? || setup_file || pipfile || pyproject
return if requirements_txt_files.any? || setup_file || setup_cfg_file || pipfile || pyproject

path = Pathname.new(File.join(directory, "requirements.txt")).
cleanpath.to_path
Expand All @@ -88,8 +90,8 @@ def setup_file
@setup_file ||= fetch_file_if_present("setup.py")
end

def setup_cfg
@setup_cfg ||= fetch_file_if_present("setup.cfg")
def setup_cfg_file
@setup_cfg_file ||= fetch_file_if_present("setup.cfg")
end

def pip_conf
Expand Down
9 changes: 7 additions & 2 deletions python/lib/dependabot/python/file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def parse
dependency_set += pipenv_dependencies if pipfile
dependency_set += poetry_dependencies if using_poetry?
dependency_set += requirement_dependencies if requirement_files.any?
dependency_set += setup_file_dependencies if setup_file
dependency_set += setup_file_dependencies if setup_file || setup_cfg_file

dependency_set.dependencies
end
Expand Down Expand Up @@ -207,8 +207,9 @@ def check_required_files
return if pipfile
return if pyproject
return if setup_file
return if setup_cfg_file

raise "No requirements.txt or setup.py!"
raise "Missing required files!"
end

def pipfile
Expand Down Expand Up @@ -248,6 +249,10 @@ def setup_file
@setup_file ||= get_original_file("setup.py")
end

def setup_cfg_file
@setup_cfg_file ||= get_original_file("setup.cfg")
end

def pip_compile_files
@pip_compile_files ||=
dependency_files.select { |f| f.name.end_with?(".in") }
Expand Down
2 changes: 2 additions & 0 deletions python/lib/dependabot/python/file_parser/setup_file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def parsed_setup_file
rescue SharedHelpers::HelperSubprocessFailed => e
raise Dependabot::DependencyFileNotEvaluatable, e.message if e.message.start_with?("InstallationError")

return [] unless setup_file

parsed_sanitized_setup_file
end

Expand Down
4 changes: 3 additions & 1 deletion python/lib/dependabot/python/file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def self.updated_files_regex
/.*\.txt$/,
/.*\.in$/,
/^setup\.py$/,
/^setup\.cfg$/,
/^pyproject\.toml$/,
/^pyproject\.lock$/
]
Expand Down Expand Up @@ -113,8 +114,9 @@ def check_required_files
return if pipfile
return if pyproject
return if get_original_file("setup.py")
return if get_original_file("setup.cfg")

raise "No requirements.txt or setup.py!"
raise "Missing required files!"
end

def pipfile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def initialize(requirements:, update_strategy:, has_lockfile:,
def updated_requirements
requirements.map do |req|
case req[:file]
when "setup.py" then updated_setup_requirement(req)
when /setup\.(?:py|cfg)$/ then updated_setup_requirement(req)
when "pyproject.toml" then updated_pyproject_requirement(req)
when "Pipfile" then updated_pipfile_requirement(req)
when /\.txt$|\.in$/ then updated_requirement(req)
Expand Down
26 changes: 26 additions & 0 deletions python/spec/dependabot/python/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
it { is_expected.to eq(true) }
end

context "with only a setup.cfg" do
let(:filenames) { %w(setup.cfg) }
it { is_expected.to eq(true) }
end

context "with only a requirements folder" do
let(:filenames) { %w(requirements) }
it { is_expected.to eq(true) }
Expand Down Expand Up @@ -211,6 +216,27 @@
end
end

context "with only a setup.cfg file" do
let(:repo_contents) do
fixture("github", "contents_python_only_setup_cfg.json")
end
before do
stub_request(:get, url + "setup.cfg?ref=sha").
with(headers: { "Authorization" => "token token" }).
to_return(
status: 200,
body: fixture("github", "setup_cfg_content.json"),
headers: { "content-type" => "application/json" }
)
end

it "fetches the setup.cfg file" do
expect(file_fetcher_instance.files.count).to eq(1)
expect(file_fetcher_instance.files.map(&:name)).
to eq(["setup.cfg"])
end
end

context "with only a Pipfile and Pipfile.lock" do
let(:repo_contents) do
fixture("github", "contents_python_only_pipfile_and_lockfile.json")
Expand Down
Loading

0 comments on commit 2606bdc

Please sign in to comment.