Skip to content

Commit

Permalink
Drop usage of pkg_resources
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarrmondragon committed Nov 6, 2023
1 parent 59f6e4d commit 64fef6e
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 37 deletions.
2 changes: 1 addition & 1 deletion fs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Python filesystem abstraction layer.
"""

__import__("pkg_resources").declare_namespace(__name__) # type: ignore
__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore

from . import path
from ._fscompat import fsdecode, fsencode
Expand Down
2 changes: 1 addition & 1 deletion fs/opener/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

# Declare fs.opener as a namespace package
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore

# Import opener modules so that `registry.install` if called on each opener
from . import appfs, ftpfs, memoryfs, osfs, tarfs, tempfs, zipfs
Expand Down
38 changes: 31 additions & 7 deletions fs/opener/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,44 @@

from __future__ import absolute_import, print_function, unicode_literals

import sys
import typing

import collections
import contextlib
import pkg_resources

from ..errors import ResourceReadOnly
from .base import Opener
from .errors import EntryPointError, UnsupportedProtocol
from .parse import parse_fs_url

if typing.TYPE_CHECKING:
from typing import Callable, Dict, Iterator, List, Text, Tuple, Type, Union
from typing import (
Callable,
Dict,
Iterator,
List,
Sequence,
Text,
Tuple,
Type,
Union,
)

from ..base import FS

if sys.version_info >= (3, 8):
# The entry_points function takes keyword arguments starting with Python 3.10
# https://docs.python.org/3.10/library/importlib.metadata.html#entry-points
from importlib import metadata as importlib_metadata
else:
import importlib_metadata


def _get_entry_points():
# type: () -> Sequence[importlib_metadata.EntryPoint]
return importlib_metadata.entry_points().get("fs.opener", [])


class Registry(object):
"""A registry for `Opener` instances."""
Expand Down Expand Up @@ -74,10 +96,7 @@ def protocols(self):
"""`list`: the list of supported protocols."""
_protocols = list(self._protocols)
if self.load_extern:
_protocols.extend(
entry_point.name
for entry_point in pkg_resources.iter_entry_points("fs.opener")
)
_protocols.extend(entry_point.name for entry_point in _get_entry_points())
_protocols = list(collections.OrderedDict.fromkeys(_protocols))
return _protocols

Expand All @@ -103,7 +122,12 @@ def get_opener(self, protocol):

if self.load_extern:
entry_point = next(
pkg_resources.iter_entry_points("fs.opener", protocol), None
(
entry_point
for entry_point in _get_entry_points()
if entry_point.name == protocol
),
None,
)
else:
entry_point = None
Expand Down
7 changes: 4 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ install_requires =
appdirs~=1.4.3
setuptools
six ~=1.10
enum34 ~=1.1.6 ; python_version < '3.4'
typing ~=3.6 ; python_version < '3.6'
backports.os ~=0.1 ; python_version < '3.0'
enum34 ~=1.1.6 ; python_version < '3.4'
typing ~=3.6 ; python_version < '3.6'
backports.os ~=0.1 ; python_version < '3.0'
importlib-metadata ~=2.0 ; python_version < '3.8'
[options.extras_require]
scandir =
Expand Down
76 changes: 51 additions & 25 deletions tests/test_opener.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
from __future__ import unicode_literals

import sys

import os
import pkg_resources
import shutil
import tempfile
import unittest
Expand All @@ -13,14 +10,19 @@
from fs.memoryfs import MemoryFS
from fs.opener import errors, registry
from fs.opener.parse import ParseResult
from fs.opener.registry import Registry
from fs.opener.registry import Registry, importlib_metadata
from fs.osfs import OSFS

try:
from unittest import mock
except ImportError:
import mock

try:
from pytest import MonkeyPatch
except ImportError:
from _pytest.monkeypatch import MonkeyPatch


class TestParse(unittest.TestCase):
def test_registry_repr(self):
Expand Down Expand Up @@ -106,34 +108,44 @@ def test_parse_params_decode(self):


class TestRegistry(unittest.TestCase):
def setUp(self):
self.monkeypatch = MonkeyPatch()

def test_protocols(self):
self.assertIsInstance(opener.registry.protocols, list)

def test_registry_protocols(self):
# Check registry.protocols list the names of all available extension
extensions = [
pkg_resources.EntryPoint("proto1", "mod1"),
pkg_resources.EntryPoint("proto2", "mod2"),
]
m = mock.MagicMock(return_value=extensions)
with mock.patch.object(
sys.modules["pkg_resources"], "iter_entry_points", new=m
):
class EntryPoint(object):
def __init__(self, name):
self.name = name

def _my_entry_points():
return {"fs.opener": [EntryPoint("proto1"), EntryPoint("proto2")]}

with self.monkeypatch.context() as m:
m.setattr(importlib_metadata, "entry_points", _my_entry_points)
self.assertIn("proto1", opener.registry.protocols)
self.assertIn("proto2", opener.registry.protocols)
self.assertNotIn("wheel", opener.registry.protocols)

def test_unknown_protocol(self):
with self.assertRaises(errors.UnsupportedProtocol):
opener.open_fs("unknown://")

def test_entry_point_load_error(self):
class EntryPoint(object):
def __init__(self, name):
self.name = name

entry_point = mock.MagicMock()
entry_point.load.side_effect = ValueError("some error")
def load(self):
raise ValueError("some error")

iter_entry_points = mock.MagicMock(return_value=iter([entry_point]))
def _my_entry_points():
return {"fs.opener": [EntryPoint("test")]}

with mock.patch("pkg_resources.iter_entry_points", iter_entry_points):
with self.monkeypatch.context() as m:
m.setattr(importlib_metadata, "entry_points", _my_entry_points)
with self.assertRaises(errors.EntryPointError) as ctx:
opener.open_fs("test://")
self.assertEqual(
Expand All @@ -144,11 +156,18 @@ def test_entry_point_type_error(self):
class NotAnOpener(object):
pass

entry_point = mock.MagicMock()
entry_point.load = mock.MagicMock(return_value=NotAnOpener)
iter_entry_points = mock.MagicMock(return_value=iter([entry_point]))
class EntryPoint(object):
def __init__(self, name):
self.name = name

def load(self):
return NotAnOpener

with mock.patch("pkg_resources.iter_entry_points", iter_entry_points):
def _my_entry_points():
return {"fs.opener": [EntryPoint("test")]}

with self.monkeypatch.context() as m:
m.setattr(importlib_metadata, "entry_points", _my_entry_points)
with self.assertRaises(errors.EntryPointError) as ctx:
opener.open_fs("test://")
self.assertEqual("entry point did not return an opener", str(ctx.exception))
Expand All @@ -161,11 +180,18 @@ def __init__(self, *args, **kwargs):
def open_fs(self, *args, **kwargs):
pass

entry_point = mock.MagicMock()
entry_point.load = mock.MagicMock(return_value=BadOpener)
iter_entry_points = mock.MagicMock(return_value=iter([entry_point]))
class EntryPoint(object):
def __init__(self, name):
self.name = name

def load(self):
return BadOpener

def _my_entry_points():
return {"fs.opener": [EntryPoint("test")]}

with mock.patch("pkg_resources.iter_entry_points", iter_entry_points):
with self.monkeypatch.context() as m:
m.setattr(importlib_metadata, "entry_points", _my_entry_points)
with self.assertRaises(errors.EntryPointError) as ctx:
opener.open_fs("test://")
self.assertEqual(
Expand Down Expand Up @@ -217,7 +243,7 @@ def tearDown(self):

def test_repr(self):
# Check __repr__ works
for entry_point in pkg_resources.iter_entry_points("fs.opener"):
for entry_point in importlib_metadata.entry_points().get("fs.opener", []):
_opener = entry_point.load()
repr(_opener())

Expand Down

0 comments on commit 64fef6e

Please sign in to comment.