Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions importlib_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
'SimplePath',
'distribution',
'distributions',
'editable',
'entry_points',
'files',
'metadata',
Expand Down Expand Up @@ -733,6 +734,18 @@ def url_req_space(req):
def origin(self):
return self._load_json('direct_url.json')

@property
def editable(self) -> bool:
"""
Whether the distribution is an editable install (:pep:`660`).

Reads ``dir_info.editable`` from the :pep:`610` ``direct_url.json``,
defaulting to ``False`` when there is no origin or the package was
not installed from a local directory.
"""
dir_info = getattr(self.origin, 'dir_info', None)
return bool(getattr(dir_info, 'editable', False))

def _load_json(self, filename):
# Deferred for performance (python/importlib_metadata#503)
import json
Expand Down Expand Up @@ -1112,6 +1125,15 @@ def version(distribution_name: str) -> str:
return distribution(distribution_name).version


def editable(distribution_name: str) -> bool:
"""Return whether the named distribution is an editable install.

:param distribution_name: The name of the distribution package to query.
:return: True if the package is installed in editable mode (:pep:`660`).
"""
return distribution(distribution_name).editable


_unique = functools.partial(
unique_everseen,
key=operator.attrgetter('_normalized_name'),
Expand Down
1 change: 1 addition & 0 deletions newsfragments/510.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ``Distribution.editable`` property and top-level ``editable()`` function reporting whether a distribution is an editable install (:pep:`660`).
15 changes: 15 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,21 @@ class DistInfoPkgEditable(DistInfoPkg):
}


class DistInfoPkgEditableDirInfo(DistInfoPkg):
"""
Package installed as editable from a local directory (:pep:`660`).
"""

files: FilesSpec = {
'distinfo_pkg-1.0.0.dist-info': {
'direct_url.json': json.dumps({
"dir_info": {"editable": True},
"url": "file:///path/to/distinfo_pkg",
})
},
}


class DistInfoPkgWithDot(OnSysPath, SiteBuilder):
files: FilesSpec = {
"pkg_dot-1.0.0.dist-info": {
Expand Down
20 changes: 20 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,23 @@ def test_origin(self):
dist = Distribution.from_name('distinfo-pkg')
assert dist.origin.url.endswith('.whl')
assert dist.origin.archive_info.hashes.sha256

def test_not_editable_from_archive(self):
# An origin without dir_info (here an archive) is not editable.
assert Distribution.from_name('distinfo-pkg').editable is False
assert importlib_metadata.editable('distinfo-pkg') is False


class EditableDirInfoDistributionTest(
fixtures.DistInfoPkgEditableDirInfo, unittest.TestCase
):
def test_editable(self):
assert Distribution.from_name('distinfo-pkg').editable is True
assert importlib_metadata.editable('distinfo-pkg') is True


class NonEditableDistributionTest(fixtures.DistInfoPkg, unittest.TestCase):
def test_not_editable_without_origin(self):
# No direct_url.json means no origin, so not editable.
assert Distribution.from_name('distinfo-pkg').editable is False
assert importlib_metadata.editable('distinfo-pkg') is False