Skip to content

Commit

Permalink
Added a method to extract top level module names from RECORD (#116)
Browse files Browse the repository at this point in the history
* Added a method to extract top level module names from RECORD
  • Loading branch information
Florian Maas authored Sep 18, 2022
1 parent cf4098b commit 83063fe
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 2 deletions.
42 changes: 40 additions & 2 deletions deptry/dependency.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import re
from typing import List

from deptry.utils import import_importlib_metadata
Expand Down Expand Up @@ -30,7 +31,9 @@ def _get_top_levels(self, name: str) -> List[str]:
top_levels = []

if self.found:
top_levels += self._get_top_level_module_names_from_metadata()
top_levels += self._get_top_level_module_names_from_top_level_txt()
if not top_levels:
top_levels += self._get_top_level_module_names_from_record_file()

top_levels.append(name.replace("-", "_").lower())
return set(top_levels)
Expand Down Expand Up @@ -66,9 +69,44 @@ def _string_for_printing(self):
else:
return " "

def _get_top_level_module_names_from_metadata(self):
def _get_top_level_module_names_from_top_level_txt(self):
"""
top-level.txt is a metadata file added by setuptools that looks as follows:
610faff656c4cfcbb4a3__mypyc
_black_version
black
blackd
blib2to3
This function extracts these names, if a top-level.txt file exists.
"""
metadata_top_levels = metadata.distribution(self.name).read_text("top_level.txt")
if metadata_top_levels:
return [x for x in metadata_top_levels.split("\n") if len(x) > 0]
else:
return []

def _get_top_level_module_names_from_record_file(self):
"""
Get the top-level module names from the RECORD file, whose contents usually look as follows:
...
black/trans.cpython-39-darwin.so,sha256=<HASH>
black/trans.py,sha256=<HASH>
blackd/__init__.py,sha256=<HASH>
blackd/__main__.py,sha256=<HASH>
...
So if no file top-level.txt is provided, we can try and extract top-levels from this file, in this case black and blackd.
"""
top_levels = []
try:
metadata_records = metadata.distribution(self.name).read_text("RECORD").split("\n")
except: # noqa: E722
return []
for line in metadata_records:
match = re.match("([a-zA-Z0-9-_]+)/", line)
if match:
top_levels.append(match.group(1))
return list(set(top_levels))
49 changes: 49 additions & 0 deletions tests/test_dependency.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from unittest.mock import patch

from deptry.dependency import Dependency
from deptry.utils import import_importlib_metadata


def test_simple_dependency():
Expand All @@ -11,3 +14,49 @@ def test_create_default_top_level_if_metadata_not_found():
dependency = Dependency("Foo-bar")
assert dependency.name == "Foo-bar"
assert dependency.top_levels == set(["foo_bar"])


def test_read_top_level_from_top_level_txt():
"""
Read the top-levels.txt file
"""

class MockDistribution:
def __init__(self):
pass

def read_text(self, file_name):
return "foo\nbar"

with patch("deptry.dependency.metadata.distribution") as mock:
mock.return_value = MockDistribution()
dependency = Dependency("Foo-bar")
assert dependency.name == "Foo-bar"
assert dependency.top_levels == set(["foo_bar", "foo", "bar"])


def test_read_top_level_from_record():
"""
Verify that if top-level.txt not found, an attempt is made to extract top-level module names from
the metadata RECORD file.
"""

class MockDistribution:
def __init__(self):
pass

def read_text(self, file_name):
if file_name == "top-level.txt":
return None
elif file_name == "RECORD":
return """black/trans.cpython-39-darwin.so,sha256=<HASH>
black/trans.py,sha256=<HASH>
blackd/__init__.py,sha256=<HASH>
blackd/__main__.py,sha256=<HASH>
"""

with patch("deptry.dependency.metadata.distribution") as mock:
mock.return_value = MockDistribution()
dependency = Dependency("Foo-bar")
assert dependency.name == "Foo-bar"
assert dependency.top_levels == set(["foo_bar", "black", "blackd"])

0 comments on commit 83063fe

Please sign in to comment.