Skip to content

Commit 6a5bfb2

Browse files
committed
change pyfilesystem2 to fsspec / morefs; bump to 0.0.13
pyfilesystem2 is no longer actively maintained, but it uses a deprecated function, leading to these warnings: ``` python3.11/site-packages/fs/__init__.py:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html ``` see e.g. PyFilesystem/pyfilesystem2#590 fortunately, fsspec and https://github.com/iterative/morefs are sufficient to cover a direct transition
1 parent 2e7d5a2 commit 6a5bfb2

File tree

4 files changed

+57
-50
lines changed

4 files changed

+57
-50
lines changed

nbs/00_core.ipynb

+32-28
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@
3333
"\n",
3434
"import dotenv\n",
3535
"import jsonschema\n",
36-
"from fs.base import FS\n",
37-
"from fs.osfs import OSFS\n",
36+
"from fsspec.spec import AbstractFileSystem\n",
37+
"from fsspec.implementations.local import LocalFileSystem\n",
38+
"from fsspec.implementations.dirfs import DirFileSystem\n",
3839
"from jsonschema import ValidationError, validate\n",
40+
"from io import StringIO\n",
3941
"\n",
4042
"logger = logging.getLogger(__name__)"
4143
]
@@ -241,35 +243,36 @@
241243
" # this is a risky value: it gets reused across instances;\n",
242244
" # the idea is to maybe set it once and use it multiple times.\n",
243245
" # but in testing this smells bad\n",
244-
" DEFAULT_STORAGE_DRIVER: FS = None # defaults to OSFS\n",
246+
" DEFAULT_STORAGE_DRIVER: AbstractFileSystem = None # defaults to DirFileSystem\n",
245247
" \n",
246248
" CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME = 'CONFIG_VALIDATOR_JSON_SCHEMA'\n",
247249
" \n",
248250
" @classmethod\n",
249251
" def get_default_storage_driver(cls):\n",
250252
" if cls.DEFAULT_STORAGE_DRIVER is None:\n",
251-
" cls.DEFAULT_STORAGE_DRIVER = OSFS(os.getcwd())\n",
253+
" cls.DEFAULT_STORAGE_DRIVER = DirFileSystem(os.getcwd())\n",
252254
" return cls.DEFAULT_STORAGE_DRIVER\n",
253255
"\n",
254256
" \n",
255257
" @classmethod\n",
256258
" def _get_maybe_abspath_driver(cls, maybe_abspath: str):\n",
257259
" if os.path.isabs(maybe_abspath): # special case\n",
258-
" return OSFS('/')\n",
260+
" return DirFileSystem('/')\n",
259261
" else:\n",
260262
" return cls.get_default_storage_driver()\n",
261263
" \n",
262264
" @classmethod\n",
263-
" def load_json(cls, json_source: Union[str, dict]=None, storage_driver: FS = None) -> dict:\n",
265+
" def load_json(cls, json_source: Union[str, dict]=None, storage_driver: AbstractFileSystem = None) -> dict:\n",
264266
" \"\"\"\n",
265267
" Convenience method to return a dictionary from either a file path or an already-loaded dictionary.\n",
266268
"\n",
267269
" Args:\n",
268270
" - `json_source` (Union[str, dict], optional): The JSON source to load.\n",
269271
" This can be a file path (str) \n",
270272
" or an already loaded dictionary (dict). \n",
271-
" - `storage_driver` (FS, optional): An instance of the storage driver used to load the JSON file. \n",
272-
" If not provided, OSFS from the current working dir is used.\n",
273+
" - `storage_driver` (AbstractFileSystem, optional): An instance of the storage driver used to\n",
274+
" load the JSON file. If not provided, DirFileSystem from the current\n",
275+
" working dir is used.\n",
273276
"\n",
274277
" Returns:\n",
275278
" dict: A dictionary that was loaded from the provided `json_source`.\n",
@@ -283,14 +286,14 @@
283286
" return json.load(ifile)\n",
284287
"\n",
285288
" @classmethod\n",
286-
" def get_default_json_schema(cls, storage_driver: FS = None) -> dict:\n",
289+
" def get_default_json_schema(cls, storage_driver: AbstractFileSystem = None) -> dict:\n",
287290
" if cls.CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME in os.environ:\n",
288291
" expected_json_schema_path = \\\n",
289292
" os.environ[cls.CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME]\n",
290293
" return cls.load_json(expected_json_schema_path, storage_driver)\n",
291294
" return None\n",
292295
"\n",
293-
" def __init__(self, json_schema: Union[str, dict]=None, storage_driver: FS=None):\n",
296+
" def __init__(self, json_schema: Union[str, dict]=None, storage_driver: AbstractFileSystem=None):\n",
294297
" \"\"\"\n",
295298
" Initialize the instance with a JSON schema and a storage driver.\n",
296299
"\n",
@@ -299,8 +302,8 @@
299302
" If no value is provided, it will fall back to looking for an environment \n",
300303
" variable corresponding to the class variable \n",
301304
" `CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME` to find a JSON schema file.\n",
302-
" - `storage_driver` (FS, optional): The storage driver to use. If no value is provided, \n",
303-
" `self.__class__.DEFAULT_STORAGE_DRIVER` is used.\n",
305+
" - `storage_driver` (AbstractFileSystem, optional): The storage driver to use. If no value is provided, \n",
306+
" `self.__class__.DEFAULT_STORAGE_DRIVER` is used.\n",
304307
"\n",
305308
" Raises:\n",
306309
" Exception: An exception is raised if no valid JSON schema is provided or found.\n",
@@ -336,7 +339,7 @@
336339
" def load_dotenv(cls,\n",
337340
" json_schema: Union[str, dict]=None,\n",
338341
" dotenv_path: str=None,\n",
339-
" storage_driver: FS=None,\n",
342+
" storage_driver: AbstractFileSystem=None,\n",
340343
" override: bool=False,\n",
341344
" ):\n",
342345
" \"\"\"\n",
@@ -349,15 +352,15 @@
349352
" (such as an environment variable or default schema) is used.\n",
350353
" - `dotenv_path` (str, optional): Path to the .env file to load the variables from.\n",
351354
" If not provided, loads an empty dict to start.\n",
352-
" - `storage_driver` (FS, optional): The storage driver to use for loading files. If not given,\n",
353-
" \".env\" will be attempted from the current working directory;\n",
354-
" if that does not exist, an empty dict will be used.\n",
355+
" - `storage_driver` (AbstractFileSystem, optional): The storage driver to use for loading files.\n",
356+
" If not given, \".env\" will be attempted from the current working\n",
357+
" directory; if that does not exist, an empty dict will be used.\n",
355358
" - `override` (bool, optional): If True, variables from the .env file or schema default override existing\n",
356359
" `os.environ` variables.\n",
357360
" \"\"\"\n",
358361
"\n",
359362
" # WARN this sidesteps storage_driver!\n",
360-
" # it will cause breakage if storage_driver != OSFS AND `.env` exists in PWD\n",
363+
" # it will cause breakage if storage_driver != DirFileSystem AND `.env` exists in PWD\n",
361364
" if dotenv_path is None:\n",
362365
" maybe_dotenv_path = dotenv.find_dotenv() # '' if not exist; else abspath\n",
363366
" if maybe_dotenv_path:\n",
@@ -369,13 +372,13 @@
369372
" if dotenv_path:\n",
370373
" dotenv_storage_driver = storage_driver or cls._get_maybe_abspath_driver(dotenv_path)\n",
371374
" with dotenv_storage_driver.open(dotenv_path) as ifile:\n",
372-
" config = dotenv.dotenv_values(stream=ifile)\n",
375+
" config = dotenv.dotenv_values(stream=StringIO(ifile.read().decode('utf-8')))\n",
373376
" \n",
374377
" if config is None:\n",
375378
" dotenv_storage_driver = storage_driver or cls.get_default_storage_driver()\n",
376379
" if dotenv_storage_driver.exists('.env'): # unlike dotenv.find_dotenv, stay relative!\n",
377380
" with dotenv_storage_driver.open('.env') as ifile:\n",
378-
" config = dotenv.dotenv_values(stream=ifile)\n",
381+
" config = dotenv.dotenv_values(stream=StringIO(ifile.read().decode('utf-8')))\n",
379382
" \n",
380383
" if config is None:\n",
381384
" config = {}\n",
@@ -580,7 +583,7 @@
580583
"outputs": [],
581584
"source": [
582585
"#| hide\n",
583-
"from fs.memoryfs import MemoryFS"
586+
"from morefs.memory import MemFS"
584587
]
585588
},
586589
{
@@ -590,14 +593,15 @@
590593
"outputs": [],
591594
"source": [
592595
"#| hide\n",
593-
"# test ability to override the storage driver (memoryfs here)\n",
596+
"# test ability to override the storage driver (MemFS here)\n",
594597
"\n",
595-
"memfs = MemoryFS()\n",
598+
"memfs = MemFS()\n",
596599
"\n",
597-
"memfs.makedirs('extra-long-directory-place', recreate=True)\n",
598-
"with memfs.open('extra-long-directory-place/schema.json', 'w') as ofile:\n",
600+
"memfs.makedirs('extra-long-directory-place', exist_ok=True)\n",
601+
"temp_config_validator_json_schema_path = 'extra-long-directory-place/schema.json'\n",
602+
"with memfs.open(temp_config_validator_json_schema_path, 'w') as ofile:\n",
599603
" ofile.write(json.dumps(example_properties_schema))\n",
600-
" os.environ['CONFIG_VALIDATOR_JSON_SCHEMA'] = ofile.name\n",
604+
" os.environ['CONFIG_VALIDATOR_JSON_SCHEMA'] = temp_config_validator_json_schema_path\n",
601605
"\n",
602606
"validator = ConfigValidator(storage_driver=memfs)\n",
603607
"validated_config = validator.load_config({\n",
@@ -615,7 +619,7 @@
615619
"\n",
616620
"# test loading dotenv from an arbitrary file\n",
617621
"\n",
618-
"memfs.makedirs('special-bespoke-location', recreate=True)\n",
622+
"memfs.makedirs('special-bespoke-location', exist_ok=True)\n",
619623
"with memfs.open('special-bespoke-location/my-own.env', 'w') as ofile:\n",
620624
" ofile.write('\\n'.join([\n",
621625
" 'string_value_with_enum=only',\n",
@@ -647,7 +651,7 @@
647651
"#| hide\n",
648652
"# test non-os FS with fallback .env path (=$PWD/.env)\n",
649653
"\n",
650-
"memfs_fallback = MemoryFS()\n",
654+
"memfs_fallback = MemFS()\n",
651655
"\n",
652656
"with memfs_fallback.open('schema.json', 'w') as ofile:\n",
653657
" ofile.write(json.dumps(example_properties_schema))\n",
@@ -714,7 +718,7 @@
714718
" }\n",
715719
" }))\n",
716720
" \n",
717-
"memfs.makedirs('precedence-test', recreate=True)\n",
721+
"memfs.makedirs('precedence-test', exist_ok=True)\n",
718722
"with memfs.open('precedence-test/.env', 'w') as ofile:\n",
719723
" ofile.write('\\n'.join([\n",
720724
" 'A_VALUE_TO_OVERRIDE=in dotenv',\n",

schematized_config/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.12"
1+
__version__ = "0.0.13"

schematized_config/core.py

+22-19
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212

1313
import dotenv
1414
import jsonschema
15-
from fs.base import FS
16-
from fs.osfs import OSFS
15+
from fsspec.spec import AbstractFileSystem
16+
from fsspec.implementations.local import LocalFileSystem
17+
from fsspec.implementations.dirfs import DirFileSystem
1718
from jsonschema import ValidationError, validate
19+
from io import StringIO
1820

1921
logger = logging.getLogger(__name__)
2022

@@ -103,35 +105,36 @@ class ConfigValidator(object):
103105
# this is a risky value: it gets reused across instances;
104106
# the idea is to maybe set it once and use it multiple times.
105107
# but in testing this smells bad
106-
DEFAULT_STORAGE_DRIVER: FS = None # defaults to OSFS
108+
DEFAULT_STORAGE_DRIVER: AbstractFileSystem = None # defaults to DirFileSystem
107109

108110
CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME = 'CONFIG_VALIDATOR_JSON_SCHEMA'
109111

110112
@classmethod
111113
def get_default_storage_driver(cls):
112114
if cls.DEFAULT_STORAGE_DRIVER is None:
113-
cls.DEFAULT_STORAGE_DRIVER = OSFS(os.getcwd())
115+
cls.DEFAULT_STORAGE_DRIVER = DirFileSystem(os.getcwd())
114116
return cls.DEFAULT_STORAGE_DRIVER
115117

116118

117119
@classmethod
118120
def _get_maybe_abspath_driver(cls, maybe_abspath: str):
119121
if os.path.isabs(maybe_abspath): # special case
120-
return OSFS('/')
122+
return DirFileSystem('/')
121123
else:
122124
return cls.get_default_storage_driver()
123125

124126
@classmethod
125-
def load_json(cls, json_source: Union[str, dict]=None, storage_driver: FS = None) -> dict:
127+
def load_json(cls, json_source: Union[str, dict]=None, storage_driver: AbstractFileSystem = None) -> dict:
126128
"""
127129
Convenience method to return a dictionary from either a file path or an already-loaded dictionary.
128130
129131
Args:
130132
- `json_source` (Union[str, dict], optional): The JSON source to load.
131133
This can be a file path (str)
132134
or an already loaded dictionary (dict).
133-
- `storage_driver` (FS, optional): An instance of the storage driver used to load the JSON file.
134-
If not provided, OSFS from the current working dir is used.
135+
- `storage_driver` (AbstractFileSystem, optional): An instance of the storage driver used to
136+
load the JSON file. If not provided, DirFileSystem from the current
137+
working dir is used.
135138
136139
Returns:
137140
dict: A dictionary that was loaded from the provided `json_source`.
@@ -145,14 +148,14 @@ def load_json(cls, json_source: Union[str, dict]=None, storage_driver: FS = None
145148
return json.load(ifile)
146149

147150
@classmethod
148-
def get_default_json_schema(cls, storage_driver: FS = None) -> dict:
151+
def get_default_json_schema(cls, storage_driver: AbstractFileSystem = None) -> dict:
149152
if cls.CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME in os.environ:
150153
expected_json_schema_path = \
151154
os.environ[cls.CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME]
152155
return cls.load_json(expected_json_schema_path, storage_driver)
153156
return None
154157

155-
def __init__(self, json_schema: Union[str, dict]=None, storage_driver: FS=None):
158+
def __init__(self, json_schema: Union[str, dict]=None, storage_driver: AbstractFileSystem=None):
156159
"""
157160
Initialize the instance with a JSON schema and a storage driver.
158161
@@ -161,8 +164,8 @@ def __init__(self, json_schema: Union[str, dict]=None, storage_driver: FS=None):
161164
If no value is provided, it will fall back to looking for an environment
162165
variable corresponding to the class variable
163166
`CONFIG_VALIDATOR_JSON_SCHEMA_ENVVAR_NAME` to find a JSON schema file.
164-
- `storage_driver` (FS, optional): The storage driver to use. If no value is provided,
165-
`self.__class__.DEFAULT_STORAGE_DRIVER` is used.
167+
- `storage_driver` (AbstractFileSystem, optional): The storage driver to use. If no value is provided,
168+
`self.__class__.DEFAULT_STORAGE_DRIVER` is used.
166169
167170
Raises:
168171
Exception: An exception is raised if no valid JSON schema is provided or found.
@@ -198,7 +201,7 @@ def load_validated_environment(cls, json_schema: Union[str, dict]=None, **kwargs
198201
def load_dotenv(cls,
199202
json_schema: Union[str, dict]=None,
200203
dotenv_path: str=None,
201-
storage_driver: FS=None,
204+
storage_driver: AbstractFileSystem=None,
202205
override: bool=False,
203206
):
204207
"""
@@ -211,15 +214,15 @@ def load_dotenv(cls,
211214
(such as an environment variable or default schema) is used.
212215
- `dotenv_path` (str, optional): Path to the .env file to load the variables from.
213216
If not provided, loads an empty dict to start.
214-
- `storage_driver` (FS, optional): The storage driver to use for loading files. If not given,
215-
".env" will be attempted from the current working directory;
216-
if that does not exist, an empty dict will be used.
217+
- `storage_driver` (AbstractFileSystem, optional): The storage driver to use for loading files.
218+
If not given, ".env" will be attempted from the current working
219+
directory; if that does not exist, an empty dict will be used.
217220
- `override` (bool, optional): If True, variables from the .env file or schema default override existing
218221
`os.environ` variables.
219222
"""
220223

221224
# WARN this sidesteps storage_driver!
222-
# it will cause breakage if storage_driver != OSFS AND `.env` exists in PWD
225+
# it will cause breakage if storage_driver != DirFileSystem AND `.env` exists in PWD
223226
if dotenv_path is None:
224227
maybe_dotenv_path = dotenv.find_dotenv() # '' if not exist; else abspath
225228
if maybe_dotenv_path:
@@ -231,13 +234,13 @@ def load_dotenv(cls,
231234
if dotenv_path:
232235
dotenv_storage_driver = storage_driver or cls._get_maybe_abspath_driver(dotenv_path)
233236
with dotenv_storage_driver.open(dotenv_path) as ifile:
234-
config = dotenv.dotenv_values(stream=ifile)
237+
config = dotenv.dotenv_values(stream=StringIO(ifile.read().decode('utf-8')))
235238

236239
if config is None:
237240
dotenv_storage_driver = storage_driver or cls.get_default_storage_driver()
238241
if dotenv_storage_driver.exists('.env'): # unlike dotenv.find_dotenv, stay relative!
239242
with dotenv_storage_driver.open('.env') as ifile:
240-
config = dotenv.dotenv_values(stream=ifile)
243+
config = dotenv.dotenv_values(stream=StringIO(ifile.read().decode('utf-8')))
241244

242245
if config is None:
243246
config = {}

settings.ini

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[DEFAULT]
22
repo = python-schematized-config
33
lib_name = python-schematized-config
4-
version = 0.0.12
4+
version = 0.0.13
55
min_python = 3.7
66
license = apache2
77
black_formatting = False
@@ -26,7 +26,7 @@ keywords = nbdev jupyter notebook python
2626
language = English
2727
status = 3
2828
user = tutankalex
29-
requirements = jsonschema>=4.17.3 python-dotenv>=1.0.0 fs>=2.4.16 fastcore>=1.5
29+
requirements = fastcore>=1.5 jsonschema>=4.17.3 python-dotenv>=1.0.0 pygtrie>=2.5.0 morefs>=0.2.2
3030
readme_nb = index.ipynb
3131
allowed_metadata_keys =
3232
allowed_cell_metadata_keys =

0 commit comments

Comments
 (0)