bw-processing 0.9__tar.gz → 0.9.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. bw_processing-0.9.6/MANIFEST.in +3 -0
  2. {bw-processing-0.9/bw_processing.egg-info → bw_processing-0.9.6}/PKG-INFO +28 -28
  3. {bw-processing-0.9 → bw_processing-0.9.6}/README.md +7 -7
  4. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/__init__.py +2 -7
  5. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/array_creation.py +2 -1
  6. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/datapackage.py +76 -108
  7. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/errors.py +2 -1
  8. bw_processing-0.9.6/bw_processing/examples/datapackage_1/another name.indices.parquet +0 -0
  9. bw_processing-0.9.6/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.data.parquet +0 -0
  10. bw_processing-0.9.6/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.distributions.parquet +0 -0
  11. bw_processing-0.9.6/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.flip.parquet +0 -0
  12. bw_processing-0.9.6/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.indices.parquet +0 -0
  13. bw_processing-0.9.6/bw_processing/examples/datapackage_1/some name.data.parquet +0 -0
  14. bw_processing-0.9.6/bw_processing/examples/datapackage_1/some name.flip.parquet +0 -0
  15. bw_processing-0.9.6/bw_processing/examples/datapackage_1/some name.indices.parquet +0 -0
  16. bw_processing-0.9.6/bw_processing/examples/datapackage_2.zip +0 -0
  17. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/examples/parquet_files.py +6 -6
  18. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/filesystem.py +3 -4
  19. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/indexing.py +7 -6
  20. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/io_helpers.py +16 -13
  21. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/io_parquet_helpers.py +12 -23
  22. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/io_pyarrow_helpers.py +3 -10
  23. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/merging.py +12 -20
  24. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/proxies.py +2 -1
  25. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/unique_fields.py +3 -2
  26. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/utils.py +13 -4
  27. {bw-processing-0.9 → bw_processing-0.9.6/bw_processing.egg-info}/PKG-INFO +28 -28
  28. bw_processing-0.9.6/bw_processing.egg-info/SOURCES.txt +57 -0
  29. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing.egg-info/requires.txt +10 -8
  30. bw_processing-0.9.6/bw_processing.egg-info/top_level.txt +1 -0
  31. bw_processing-0.9.6/docs/conf.py +109 -0
  32. bw_processing-0.9.6/pyproject.toml +108 -0
  33. bw_processing-0.9.6/setup.cfg +4 -0
  34. {bw-processing-0.9 → bw_processing-0.9.6}/tests/test_utils.py +1 -6
  35. bw-processing-0.9/MANIFEST.in +0 -3
  36. bw-processing-0.9/bw_processing.egg-info/SOURCES.txt +0 -39
  37. bw-processing-0.9/bw_processing.egg-info/not-zip-safe +0 -1
  38. bw-processing-0.9/bw_processing.egg-info/top_level.txt +0 -5
  39. bw-processing-0.9/docs/conf.py +0 -48
  40. bw-processing-0.9/pyproject.toml +0 -3
  41. bw-processing-0.9/setup.cfg +0 -87
  42. bw-processing-0.9/tests/fixtures/basic_arrays.py +0 -42
  43. bw-processing-0.9/tests/helpers/__init__.py +0 -6
  44. bw-processing-0.9/tests/helpers/basic_array_helpers.py +0 -33
  45. {bw-processing-0.9 → bw_processing-0.9.6}/LICENSE +0 -0
  46. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/constants.py +0 -0
  47. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/examples/__init__.py +0 -0
  48. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/examples/interfaces.py +0 -0
  49. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing/examples/simple.zip +0 -0
  50. {bw-processing-0.9 → bw_processing-0.9.6}/bw_processing.egg-info/dependency_links.txt +0 -0
  51. {bw-processing-0.9 → bw_processing-0.9.6}/dev/calculation_package.py +0 -0
  52. {bw-processing-0.9 → bw_processing-0.9.6}/dev/loading.py +0 -0
  53. {bw-processing-0.9 → bw_processing-0.9.6}/dev/processed_package.py +0 -0
  54. {bw-processing-0.9 → bw_processing-0.9.6}/dev/resources.py +0 -0
  55. {bw-processing-0.9 → bw_processing-0.9.6}/dev/speed_tests.py +0 -0
@@ -0,0 +1,3 @@
1
+ include bw_processing/examples/*.zip
2
+ include bw_processing/examples/datapackage_1/*.parquet
3
+ recursive-exclude dev *
@@ -1,43 +1,43 @@
1
1
  Metadata-Version: 2.1
2
- Name: bw-processing
3
- Version: 0.9
4
- Summary: Tools to create structured arrays in a common format
5
- Home-page: https://github.com/brightway-lca/bw_processing
6
- Author: Chris Mutel
7
- Author-email: <cmutel@gmail.com>
8
- Maintainer: Chris Mutel
9
- Maintainer-email: <cmutel@gmail.com>
10
- License: BSD-3-Clause
2
+ Name: bw_processing
3
+ Version: 0.9.6
4
+ Summary: Foo
5
+ Author-email: Chris Mutel <cmutel@gmail.com>
6
+ Maintainer-email: Chris Mutel <cmutel@gmail.com>
11
7
  Project-URL: source, https://github.com/brightway-lca/bw_processing
12
8
  Project-URL: homepage, https://github.com/brightway-lca/bw_processing
13
9
  Project-URL: tracker, https://github.com/brightway-lca/bw_processing/issues
14
- Keywords: "brightway","development"
15
- Platform: any
16
10
  Classifier: Development Status :: 4 - Beta
17
11
  Classifier: Programming Language :: Python
18
12
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
13
  Classifier: Programming Language :: Python :: 3.9
21
14
  Classifier: Programming Language :: Python :: 3.10
22
15
  Classifier: Programming Language :: Python :: 3.11
23
- Requires-Python: >=3.8
24
- Description-Content-Type: text/markdown; charset=UTF-8
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Natural Language :: English
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
25
22
  License-File: LICENSE
26
- Requires-Dist: fs
23
+ Requires-Dist: fsspec
24
+ Requires-Dist: morefs
27
25
  Requires-Dist: numpy
28
26
  Requires-Dist: pandas
29
27
  Provides-Extra: testing
30
- Requires-Dist: setuptools; extra == "testing"
28
+ Requires-Dist: bw_processing; extra == "testing"
29
+ Requires-Dist: pyarrow; extra == "testing"
31
30
  Requires-Dist: pytest; extra == "testing"
32
31
  Requires-Dist: pytest-cov; extra == "testing"
33
- Requires-Dist: pyarrow; extra == "testing"
32
+ Requires-Dist: python-coveralls; extra == "testing"
34
33
  Provides-Extra: dev
34
+ Requires-Dist: build; extra == "dev"
35
35
  Requires-Dist: pre-commit; extra == "dev"
36
36
  Requires-Dist: pylint; extra == "dev"
37
- Provides-Extra: docs
38
- Requires-Dist: sphinx; extra == "docs"
39
- Requires-Dist: myst_parser; extra == "docs"
40
- Requires-Dist: furo; extra == "docs"
37
+ Requires-Dist: pytest; extra == "dev"
38
+ Requires-Dist: pytest-cov; extra == "dev"
39
+ Requires-Dist: pytest-randomly; extra == "dev"
40
+ Requires-Dist: setuptools; extra == "dev"
41
41
 
42
42
  # bw-processing
43
43
 
@@ -46,10 +46,10 @@ Library for storing numeric data for use in matrix-based calculations. Designed
46
46
  [![PyPI](https://img.shields.io/pypi/v/bw-processing.svg)][pypi status]
47
47
  [![Status](https://img.shields.io/pypi/status/bw-processing.svg)][pypi status]
48
48
  [![Python Version](https://img.shields.io/pypi/pyversions/bw-processing)][pypi status]
49
- [![License](https://img.shields.io/pypi/l/bw-processing)][license]
49
+ [![License](https://img.shields.io/pypi/l/bw_processing)][license]
50
50
 
51
51
  [![Read the documentation at https://bw-processing.readthedocs.io/](https://img.shields.io/readthedocs/bw-processing/latest.svg?label=Read%20the%20Docs)][read the docs]
52
- [![Tests](https://github.com/brightway-lca/bw-processing/actions/workflows/python-test.yml/badge.svg)][tests]
52
+ [![Testing](https://github.com/brightway-lca/bw_processing/actions/workflows/python-test.yml/badge.svg)][tests]
53
53
  [![Codecov](https://codecov.io/gh/brightway-lca/bw-processing/branch/main/graph/badge.svg)][codecov]
54
54
 
55
55
  [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)][pre-commit]
@@ -57,7 +57,7 @@ Library for storing numeric data for use in matrix-based calculations. Designed
57
57
 
58
58
  [pypi status]: https://pypi.org/project/bw-processing/
59
59
  [read the docs]: https://bw-processing.readthedocs.io/
60
- [tests]: https://github.com/brightway-lca/bw-processing/actions?workflow=Tests
60
+ [tests]: https://github.com/brightway-lca/bw_processing/actions/workflows/python-test.yml
61
61
  [codecov]: https://app.codecov.io/gh/brightway-lca/bw-processing
62
62
  [pre-commit]: https://github.com/pre-commit/pre-commit
63
63
  [black]: https://github.com/psf/black
@@ -148,10 +148,10 @@ class ExampleArrayInterface:
148
148
  Serialized datapackages cannot contain executable code, both because of our chosen data formats, and for security reasons. Therefore, when loading a datapackage with an interface, that interface object needs to be reconstituted as Python code - we call this cycle dehydration and rehydration. Dehydration happens automatically when a datapackage is finalized with `finalize_serialization()`, but rehydration needs to be done manually using `rehydrate_interface()`. For example:
149
149
 
150
150
  ```python
151
- from fs.zipfs import ZipFS
151
+ from fsspec.implementations.zip import ZipFileSystem
152
152
  from bw_processing import load_datapackage
153
153
 
154
- my_dp = load_datapackage(ZipFS("some-path.zip"))
154
+ my_dp = load_datapackage(ZipFileSystem("some-path.zip"))
155
155
  my_dp.rehydrate_interface("some-resource-name", ExampleVectorInterface())
156
156
  ```
157
157
 
@@ -160,7 +160,7 @@ You can list the dehydrated interfaces present with `.dehydrated_interfaces()`.
160
160
  You can store useful information for the interface object initialization under the resource key `config`. This can be used in instantiating an interface if you pass `initialize_with_config`:
161
161
 
162
162
  ```python
163
- from fs.zipfs import ZipFS
163
+ from fsspec.implementations.zip import ZipFileSystem
164
164
  from bw_processing import load_datapackage
165
165
  import requests
166
166
  import numpy as np
@@ -174,7 +174,7 @@ class MyInterface:
174
174
  return np.array(requests.get(self.url).json())
175
175
 
176
176
 
177
- my_dp = load_datapackage(ZipFS("some-path.zip"))
177
+ my_dp = load_datapackage(ZipFileSystem("some-path.zip"))
178
178
  data_obj, resource_metadata = my_dp.get_resource("some-interface")
179
179
  print(resource_metadata['config'])
180
180
  >>> {"url": "example.com"}
@@ -5,10 +5,10 @@ Library for storing numeric data for use in matrix-based calculations. Designed
5
5
  [![PyPI](https://img.shields.io/pypi/v/bw-processing.svg)][pypi status]
6
6
  [![Status](https://img.shields.io/pypi/status/bw-processing.svg)][pypi status]
7
7
  [![Python Version](https://img.shields.io/pypi/pyversions/bw-processing)][pypi status]
8
- [![License](https://img.shields.io/pypi/l/bw-processing)][license]
8
+ [![License](https://img.shields.io/pypi/l/bw_processing)][license]
9
9
 
10
10
  [![Read the documentation at https://bw-processing.readthedocs.io/](https://img.shields.io/readthedocs/bw-processing/latest.svg?label=Read%20the%20Docs)][read the docs]
11
- [![Tests](https://github.com/brightway-lca/bw-processing/actions/workflows/python-test.yml/badge.svg)][tests]
11
+ [![Testing](https://github.com/brightway-lca/bw_processing/actions/workflows/python-test.yml/badge.svg)][tests]
12
12
  [![Codecov](https://codecov.io/gh/brightway-lca/bw-processing/branch/main/graph/badge.svg)][codecov]
13
13
 
14
14
  [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)][pre-commit]
@@ -16,7 +16,7 @@ Library for storing numeric data for use in matrix-based calculations. Designed
16
16
 
17
17
  [pypi status]: https://pypi.org/project/bw-processing/
18
18
  [read the docs]: https://bw-processing.readthedocs.io/
19
- [tests]: https://github.com/brightway-lca/bw-processing/actions?workflow=Tests
19
+ [tests]: https://github.com/brightway-lca/bw_processing/actions/workflows/python-test.yml
20
20
  [codecov]: https://app.codecov.io/gh/brightway-lca/bw-processing
21
21
  [pre-commit]: https://github.com/pre-commit/pre-commit
22
22
  [black]: https://github.com/psf/black
@@ -107,10 +107,10 @@ class ExampleArrayInterface:
107
107
  Serialized datapackages cannot contain executable code, both because of our chosen data formats, and for security reasons. Therefore, when loading a datapackage with an interface, that interface object needs to be reconstituted as Python code - we call this cycle dehydration and rehydration. Dehydration happens automatically when a datapackage is finalized with `finalize_serialization()`, but rehydration needs to be done manually using `rehydrate_interface()`. For example:
108
108
 
109
109
  ```python
110
- from fs.zipfs import ZipFS
110
+ from fsspec.implementations.zip import ZipFileSystem
111
111
  from bw_processing import load_datapackage
112
112
 
113
- my_dp = load_datapackage(ZipFS("some-path.zip"))
113
+ my_dp = load_datapackage(ZipFileSystem("some-path.zip"))
114
114
  my_dp.rehydrate_interface("some-resource-name", ExampleVectorInterface())
115
115
  ```
116
116
 
@@ -119,7 +119,7 @@ You can list the dehydrated interfaces present with `.dehydrated_interfaces()`.
119
119
  You can store useful information for the interface object initialization under the resource key `config`. This can be used in instantiating an interface if you pass `initialize_with_config`:
120
120
 
121
121
  ```python
122
- from fs.zipfs import ZipFS
122
+ from fsspec.implementations.zip import ZipFileSystem
123
123
  from bw_processing import load_datapackage
124
124
  import requests
125
125
  import numpy as np
@@ -133,7 +133,7 @@ class MyInterface:
133
133
  return np.array(requests.get(self.url).json())
134
134
 
135
135
 
136
- my_dp = load_datapackage(ZipFS("some-path.zip"))
136
+ my_dp = load_datapackage(ZipFileSystem("some-path.zip"))
137
137
  data_obj, resource_metadata = my_dp.get_resource("some-interface")
138
138
  print(resource_metadata['config'])
139
139
  >>> {"url": "example.com"}
@@ -26,16 +26,11 @@ __all__ = (
26
26
  "UndefinedInterface",
27
27
  )
28
28
 
29
- __version__ = "0.9"
29
+ __version__ = "0.9.6"
30
30
 
31
31
 
32
32
  from .array_creation import create_array, create_structured_array
33
- from .constants import (
34
- DEFAULT_LICENSES,
35
- INDICES_DTYPE,
36
- UNCERTAINTY_DTYPE,
37
- MatrixSerializeFormat,
38
- )
33
+ from .constants import DEFAULT_LICENSES, INDICES_DTYPE, UNCERTAINTY_DTYPE, MatrixSerializeFormat
39
34
  from .datapackage import (
40
35
  Datapackage,
41
36
  DatapackageBase,
@@ -68,7 +68,8 @@ def create_structured_array(iterable, dtype, nrows=None, sort=False, sort_fields
68
68
 
69
69
  ``iterable`` can be data already in memory, or a generator.
70
70
 
71
- ``nrows`` can be supplied, if known. If ``iterable`` has a length, it will be determined automatically. If ``nrows`` is not known, this function generates chunked arrays until ``iterable`` is exhausted, and concatenates them."""
71
+ ``nrows`` can be supplied, if known. If ``iterable`` has a length, it will be determined automatically. If ``nrows`` is not known, this function generates chunked arrays until ``iterable`` is exhausted, and concatenates them.
72
+ """
72
73
  if nrows or hasattr(iterable, "__len__"):
73
74
  if not nrows:
74
75
  nrows = len(iterable)
@@ -6,9 +6,10 @@ from typing import Any, Dict, List, Optional, Union
6
6
 
7
7
  import numpy as np
8
8
  import pandas as pd
9
- from fs.base import FS
10
- from fs.errors import ResourceNotFound
11
- from fs.memoryfs import MemoryFS
9
+ from fsspec import AbstractFileSystem
10
+
11
+ # Use this instead of fsspec MemoryFileSystem because that is a singleton!?
12
+ from morefs.dict import DictFS
12
13
 
13
14
  from .constants import (
14
15
  DEFAULT_LICENSES,
@@ -31,7 +32,7 @@ from .errors import (
31
32
  from .filesystem import clean_datapackage_name
32
33
  from .io_helpers import file_reader, file_writer
33
34
  from .proxies import Proxy, UndefinedInterface
34
- from .utils import check_name, check_suffix, load_bytes, resolve_dict_iterator
35
+ from .utils import check_name, check_suffix, load_bytes, resolve_dict_iterator, utc_now
35
36
 
36
37
 
37
38
  class DatapackageBase(ABC):
@@ -109,15 +110,13 @@ class DatapackageBase(ABC):
109
110
  def del_resource(self, name_or_index: Union[str, int]) -> None:
110
111
  """Remove a resource, and delete its data file, if any."""
111
112
  if self._modified:
112
- raise PotentialInconsistency(
113
- "Datapackage is modified; save modifications or reload"
114
- )
113
+ raise PotentialInconsistency("Datapackage is modified; save modifications or reload")
115
114
 
116
115
  index = self._get_index(name_or_index)
117
116
 
118
117
  try:
119
- self.fs.remove(self.resources[index]["path"])
120
- except (KeyError, ResourceNotFound):
118
+ self.fs.rm(self.resources[index]["path"])
119
+ except (KeyError, FileNotFoundError):
121
120
  # Interface has no path
122
121
  pass
123
122
 
@@ -127,28 +126,21 @@ class DatapackageBase(ABC):
127
126
  def del_resource_group(self, name: str) -> None:
128
127
  """Remove a resource group, and delete its data files, if any.
129
128
 
130
- Use ``exclude_resource_group`` if you want to keep the underlying resource in the filesystem."""
129
+ Use ``exclude_resource_group`` if you want to keep the underlying resource in the filesystem.
130
+ """
131
131
  if self._modified:
132
- raise PotentialInconsistency(
133
- "Datapackage is modified; save modifications or reload"
134
- )
132
+ raise PotentialInconsistency("Datapackage is modified; save modifications or reload")
135
133
 
136
- indices = [
137
- i
138
- for i, resource in enumerate(self.resources)
139
- if resource.get("group") == name
140
- ]
134
+ indices = [i for i, resource in enumerate(self.resources) if resource.get("group") == name]
141
135
 
142
136
  for obj in (obj for i, obj in enumerate(self.resources) if i in indices):
143
137
  try:
144
- self.fs.remove(obj["path"])
145
- except (KeyError, ResourceNotFound):
138
+ self.fs.rm(obj["path"])
139
+ except (KeyError, FileNotFoundError):
146
140
  # Interface has no path
147
141
  pass
148
142
 
149
- self.resources = [
150
- obj for i, obj in enumerate(self.resources) if i not in indices
151
- ]
143
+ self.resources = [obj for i, obj in enumerate(self.resources) if i not in indices]
152
144
  self.data = [obj for i, obj in enumerate(self.data) if i not in indices]
153
145
 
154
146
  def get_resource(self, name_or_index: Union[str, int]) -> (Any, dict):
@@ -181,14 +173,13 @@ class DatapackageBase(ABC):
181
173
 
182
174
  All included objects are the same as in the original data package, i.e. no copies are made. No checks are made to ensure consistency with modifications to the original datapackage after the creation of this filtered datapackage.
183
175
 
184
- This method was introduced to allow for the efficient construction of matrices; each datapackage can have data for multiple matrices, and we can then create filtered datapackages which exclusively have data for the matrix of interest. As such, they should be considered read-only, though this is not enforced."""
176
+ This method was introduced to allow for the efficient construction of matrices; each datapackage can have data for multiple matrices, and we can then create filtered datapackages which exclusively have data for the matrix of interest. As such, they should be considered read-only, though this is not enforced.
177
+ """
185
178
  fdp = FilteredDatapackage()
186
179
  fdp.fs = self.fs
187
180
  fdp.metadata = {k: v for k, v in self.metadata.items() if k != "resources"}
188
181
  fdp.metadata["resources"] = []
189
- to_include = [
190
- i for i, resource in enumerate(self.resources) if resource.get(key) == value
191
- ]
182
+ to_include = [i for i, resource in enumerate(self.resources) if resource.get(key) == value]
192
183
  fdp.data = [o for i, o in enumerate(self.data) if i in to_include]
193
184
  fdp.resources = [o for i, o in enumerate(self.resources) if i in to_include]
194
185
  if hasattr(self, "indexer"):
@@ -223,17 +214,13 @@ class DatapackageBase(ABC):
223
214
  if any(resource.get(key) != value for key, value in filters.items())
224
215
  ]
225
216
  fdp.data = [o for i, o in enumerate(self.data) if i in indices_to_include]
226
- fdp.resources = [
227
- o for i, o in enumerate(self.resources) if i in indices_to_include
228
- ]
217
+ fdp.resources = [o for i, o in enumerate(self.resources) if i in indices_to_include]
229
218
  return fdp
230
219
 
231
220
  def _dehydrate_interfaces(self) -> None:
232
221
  """Substitute an interface resource with ``UndefinedInterface``, in preparation for finalizing data on disk."""
233
222
  interface_indices = [
234
- index
235
- for index, obj in enumerate(self.resources)
236
- if obj["profile"] == "interface"
223
+ index for index, obj in enumerate(self.resources) if obj["profile"] == "interface"
237
224
  ]
238
225
 
239
226
  for index in interface_indices:
@@ -299,7 +286,7 @@ class Datapackage(DatapackageBase):
299
286
  return hash((self.fs, self.metadata))
300
287
 
301
288
  def __eq__(self, other):
302
- return (self.fs, self.metadata) == (other.fs, other.metadata)
289
+ return (id(self.fs), self.metadata) == (id(other.fs), other.metadata)
303
290
 
304
291
  def _check_length_consistency(self) -> None:
305
292
  if len(self.resources) != len(self.data):
@@ -310,7 +297,7 @@ class Datapackage(DatapackageBase):
310
297
  )
311
298
 
312
299
  def _load(
313
- self, fs: FS, mmap_mode: Optional[str] = None, proxy: bool = False
300
+ self, fs: AbstractFileSystem, mmap_mode: Optional[str] = None, proxy: bool = False
314
301
  ) -> None:
315
302
  self.fs = fs
316
303
  self.metadata = file_reader(
@@ -336,7 +323,7 @@ class Datapackage(DatapackageBase):
336
323
 
337
324
  def _create(
338
325
  self,
339
- fs: Optional[FS],
326
+ fs: Optional[AbstractFileSystem],
340
327
  name: Optional[str],
341
328
  id_: Optional[str],
342
329
  metadata: Optional[dict],
@@ -356,7 +343,7 @@ class Datapackage(DatapackageBase):
356
343
  name = clean_datapackage_name(name or uuid.uuid4().hex)
357
344
  check_name(name)
358
345
 
359
- self.fs = fs or MemoryFS()
346
+ self.fs = fs or DictFS()
360
347
  if not isinstance(matrix_serialize_format_type, MatrixSerializeFormat):
361
348
  raise TypeError(
362
349
  f"Matrix serialize format type ({matrix_serialize_format_type}) not recognized!"
@@ -369,7 +356,7 @@ class Datapackage(DatapackageBase):
369
356
  "id": id_ or uuid.uuid4().hex,
370
357
  "licenses": (metadata or {}).get("licenses", DEFAULT_LICENSES),
371
358
  "resources": [],
372
- "created": datetime.datetime.utcnow().isoformat("T") + "Z",
359
+ "created": utc_now().isoformat("T") + "Z",
373
360
  "combinatorial": combinatorial,
374
361
  "sequential": sequential,
375
362
  "seed": seed,
@@ -386,7 +373,7 @@ class Datapackage(DatapackageBase):
386
373
  def finalize_serialization(self) -> None:
387
374
  if self._finalized:
388
375
  raise Closed("Datapackage already finalized")
389
- elif isinstance(self.fs, MemoryFS):
376
+ elif isinstance(self.fs, DictFS):
390
377
  raise ValueError("In-memory file systems can't be serialized")
391
378
 
392
379
  self._dehydrate_interfaces()
@@ -398,7 +385,8 @@ class Datapackage(DatapackageBase):
398
385
  resource="datapackage.json",
399
386
  mimetype="application/json",
400
387
  )
401
- self.fs.close()
388
+ if hasattr(self.fs, "close"):
389
+ self.fs.close()
402
390
  self._finalized = True
403
391
 
404
392
  def _prepare_modifications(self) -> None:
@@ -469,9 +457,7 @@ class Datapackage(DatapackageBase):
469
457
 
470
458
  # Check lengths
471
459
 
472
- kwargs.update(
473
- {"matrix": matrix, "category": "vector", "nrows": len(indices_array)}
474
- )
460
+ kwargs.update({"matrix": matrix, "category": "vector", "nrows": len(indices_array)})
475
461
  name = self._prepare_name(name)
476
462
 
477
463
  indices_array = load_bytes(indices_array)
@@ -514,9 +500,7 @@ class Datapackage(DatapackageBase):
514
500
  if distributions_array is not None:
515
501
  distributions_array = load_bytes(distributions_array)
516
502
  # If no uncertainty, don't need to store it
517
- if (distributions_array["uncertainty_type"] < 2).sum() < len(
518
- distributions_array
519
- ):
503
+ if (distributions_array["uncertainty_type"] < 2).sum() < len(distributions_array):
520
504
  if distributions_array.shape != indices_array.shape:
521
505
  raise ShapeMismatch(
522
506
  "`distributions_array` shape ({}) doesn't match `indices_array` ({}).".format(
@@ -540,9 +524,7 @@ class Datapackage(DatapackageBase):
540
524
  if flip_array.sum():
541
525
  if flip_array.dtype != bool:
542
526
  raise WrongDatatype(
543
- "`flip_array` dtype is {}, but must be `bool`".format(
544
- flip_array.dtype
545
- )
527
+ "`flip_array` dtype is {}, but must be `bool`".format(flip_array.dtype)
546
528
  )
547
529
  elif flip_array.shape != indices_array.shape:
548
530
  raise ShapeMismatch(
@@ -577,9 +559,7 @@ class Datapackage(DatapackageBase):
577
559
  """ """
578
560
  self._prepare_modifications()
579
561
 
580
- kwargs.update(
581
- {"matrix": matrix, "category": "array", "nrows": len(indices_array)}
582
- )
562
+ kwargs.update({"matrix": matrix, "category": "array", "nrows": len(indices_array)})
583
563
  name = self._prepare_name(name)
584
564
 
585
565
  indices_array = load_bytes(indices_array)
@@ -624,9 +604,7 @@ class Datapackage(DatapackageBase):
624
604
  if flip_array.sum():
625
605
  if flip_array.dtype != bool:
626
606
  raise WrongDatatype(
627
- "`flip_array` dtype is {}, but must be `bool`".format(
628
- flip_array.dtype
629
- )
607
+ "`flip_array` dtype is {}, but must be `bool`".format(flip_array.dtype)
630
608
  )
631
609
  elif flip_array.shape != indices_array.shape:
632
610
  raise ShapeMismatch(
@@ -714,9 +692,7 @@ class Datapackage(DatapackageBase):
714
692
  meta_type: Optional[str] = None,
715
693
  **kwargs,
716
694
  ) -> None:
717
- assert (
718
- array.ndim <= 2
719
- ), f"Numpy array should be of dim 2 or less instead of {array.ndim}!"
695
+ assert array.ndim <= 2, f"Numpy array should be of dim 2 or less instead of {array.ndim}!"
720
696
 
721
697
  if matrix_serialize_format_type is None:
722
698
  # use instance default serialization format
@@ -739,13 +715,13 @@ class Datapackage(DatapackageBase):
739
715
  f"Matrix serialize format type {matrix_serialize_format_type} is not recognized!"
740
716
  )
741
717
 
742
- if not isinstance(self.fs, MemoryFS):
718
+ if not isinstance(self.fs, DictFS):
743
719
  file_writer(
744
720
  data=array,
745
721
  fs=self.fs,
746
722
  resource=filename,
747
723
  mimetype="application/octet-stream",
748
- matrix_serialize_format_type=matrix_serialize_format_type, # NIKO
724
+ matrix_serialize_format_type=matrix_serialize_format_type,
749
725
  meta_object=meta_object,
750
726
  meta_type=meta_type,
751
727
  )
@@ -791,9 +767,7 @@ class Datapackage(DatapackageBase):
791
767
  ) -> None:
792
768
  self._prepare_modifications()
793
769
 
794
- kwargs.update(
795
- {"matrix": matrix, "category": "vector", "nrows": len(indices_array)}
796
- )
770
+ kwargs.update({"matrix": matrix, "category": "vector", "nrows": len(indices_array)})
797
771
  name = self._prepare_name(name)
798
772
 
799
773
  indices_array = load_bytes(indices_array)
@@ -813,9 +787,7 @@ class Datapackage(DatapackageBase):
813
787
  if flip_array.sum():
814
788
  if flip_array.dtype != bool:
815
789
  raise WrongDatatype(
816
- "`flip_array` dtype is {}, but must be `bool`".format(
817
- flip_array.dtype
818
- )
790
+ "`flip_array` dtype is {}, but must be `bool`".format(flip_array.dtype)
819
791
  )
820
792
  elif flip_array.shape != indices_array.shape:
821
793
  raise ShapeMismatch(
@@ -863,9 +835,7 @@ class Datapackage(DatapackageBase):
863
835
  if isinstance(flip_array, np.ndarray) and not flip_array.sum():
864
836
  flip_array = None
865
837
 
866
- kwargs.update(
867
- {"matrix": matrix, "category": "array", "nrows": len(indices_array)}
868
- )
838
+ kwargs.update({"matrix": matrix, "category": "array", "nrows": len(indices_array)})
869
839
  name = self._prepare_name(name)
870
840
 
871
841
  indices_array = load_bytes(indices_array)
@@ -885,9 +855,7 @@ class Datapackage(DatapackageBase):
885
855
  if flip_array.sum():
886
856
  if flip_array.dtype != bool:
887
857
  raise WrongDatatype(
888
- "`flip_array` dtype is {}, but must be `bool`".format(
889
- flip_array.dtype
890
- )
858
+ "`flip_array` dtype is {}, but must be `bool`".format(flip_array.dtype)
891
859
  )
892
860
  elif flip_array.shape != indices_array.shape:
893
861
  raise ShapeMismatch(
@@ -975,9 +943,7 @@ class Datapackage(DatapackageBase):
975
943
  )
976
944
  self.resources.append(kwargs)
977
945
 
978
- def add_json_metadata(
979
- self, *, data: Any, valid_for: str, name: str = None, **kwargs
980
- ) -> None:
946
+ def add_json_metadata(self, *, data: Any, valid_for: str, name: str = None, **kwargs) -> None:
981
947
  """Add an iterable metadata object to be stored as a JSON file.
982
948
 
983
949
  The purpose of storing metadata is to enable data exchange; therefore, this method assumes that data is written to disk.
@@ -1013,9 +979,7 @@ class Datapackage(DatapackageBase):
1013
979
 
1014
980
  filename = check_suffix(name, ".json")
1015
981
 
1016
- file_writer(
1017
- data=data, fs=self.fs, resource=filename, mimetype="application/json"
1018
- )
982
+ file_writer(data=data, fs=self.fs, resource=filename, mimetype="application/json")
1019
983
  self.data.append(data)
1020
984
 
1021
985
  kwargs.update(
@@ -1033,7 +997,7 @@ class Datapackage(DatapackageBase):
1033
997
 
1034
998
 
1035
999
  def create_datapackage(
1036
- fs: Optional[FS] = None,
1000
+ fs: Optional[AbstractFileSystem] = None,
1037
1001
  name: Optional[str] = None,
1038
1002
  id_: Optional[str] = None,
1039
1003
  metadata: Optional[dict] = None,
@@ -1046,24 +1010,23 @@ def create_datapackage(
1046
1010
  ) -> Datapackage:
1047
1011
  """Create a new data package.
1048
1012
 
1049
- All arguments are optional; if a `PyFilesystem2 <https://docs.pyfilesystem.org/en/latest/>`__ filesystem is not provided, a `MemoryFS <https://docs.pyfilesystem.org/en/latest/reference/memoryfs.html>`__ will be used.
1013
+ All arguments are optional; if a `fsspec <https://filesystem-spec.readthedocs.io/en/latest/>`__ filesystem is not provided, an in-memory `DictFS <https://github.com/iterative/morefs?tab=readme-ov-file#dictfs>`__ will be used.
1050
1014
 
1051
1015
  All metadata elements should follow the `datapackage specification <https://frictionlessdata.io/specs/data-package/>`__.
1052
1016
 
1053
1017
  Licenses are specified as a list in ``metadata``. The default license is the `Open Data Commons Public Domain Dedication and License v1.0 <http://opendatacommons.org/licenses/pddl/>`__.
1054
1018
 
1055
1019
  Args:
1056
-
1057
- * fs: A `Filesystem`, optional. A new `MemoryFS` is used if not provided.
1058
- * name: `str`, optional. A new uuid is used if not provided.
1059
- * id_. `str`, optional. A new uuid is used if not provided.
1060
- * metadata. `dict`, optional. Metadata dictionary following datapackage specification; see above.
1061
- * combinatorial. `bool`, default `False.: Policy on how to sample columns across multiple data arrays; see readme.
1062
- * sequential. `bool`, default `False.: Policy on how to sample columns in data arrays; see readme.
1063
- * seed. `int`, optional. Seed to use in random number generator.
1064
- * sum_intra_duplicates. `bool`, default `True`. Should duplicate elements in a single data resource be summed together, or should the last value replace previous values.
1065
- * sum_inter_duplicates. `bool`, default `False`. Should duplicate elements in across data resources be summed together, or should the last value replace previous values. Order of data resources is given by the order they are added to the data package.
1066
- * matrix_serialize_format_type. `MatrixSerializeFormat`, default `MatrixSerializeFormat.NUMPY`. Matrix serialization format type.
1020
+ * fs: A ``Filesystem``, optional. A new ``DictFS`` is used if not provided.
1021
+ * name: ``str``, optional. A new uuid is used if not provided.
1022
+ * `id_`: ``str``, optional. A new uuid is used if not provided.
1023
+ * metadata: ``dict``, optional. Metadata dictionary following datapackage specification; see above.
1024
+ * combinatorial: ``bool``, default ``False`` .: Policy on how to sample columns across multiple data arrays; see readme.
1025
+ * sequential: ``bool``, default ``False`` .: Policy on how to sample columns in data arrays; see readme.
1026
+ * seed: ``int``, optional. Seed to use in random number generator.
1027
+ * sum_intra_duplicates: ``bool``, default ``True``. Should duplicate elements in a single data resource be summed together, or should the last value replace previous values.
1028
+ * sum_inter_duplicates: ``bool``, default ``False``. Should duplicate elements in across data resources be summed together, or should the last value replace previous values. Order of data resources is given by the order they are added to the data package.
1029
+ * matrix_serialize_format_type: ``MatrixSerializeFormat``, default ``MatrixSerializeFormat.NUMPY``. Matrix serialization format type.
1067
1030
 
1068
1031
  Returns:
1069
1032
 
@@ -1087,7 +1050,7 @@ def create_datapackage(
1087
1050
 
1088
1051
 
1089
1052
  def load_datapackage(
1090
- fs_or_obj: Union[DatapackageBase, FS],
1053
+ fs_or_obj: Union[DatapackageBase, AbstractFileSystem],
1091
1054
  mmap_mode: Optional[str] = None,
1092
1055
  proxy: bool = False,
1093
1056
  ) -> Datapackage:
@@ -1099,9 +1062,9 @@ def load_datapackage(
1099
1062
 
1100
1063
  Args:
1101
1064
 
1102
- * fs_or_obj. A `Filesystem` or an instance of `DatapackageBase`.
1103
- * mmap_mode. `str`, optional. Define memory mapping mode to use when loading Numpy arrays.
1104
- * proxy. bool, default `False`. Load proxies instead of complete Numpy arrays; see above.
1065
+ * fs_or_obj: A `Filesystem` or an instance of `DatapackageBase`.
1066
+ * mmap_mode: `str`, optional. Define memory mapping mode to use when loading Numpy arrays.
1067
+ * proxy: bool, default `False`. Load proxies instead of complete Numpy arrays; see above.
1105
1068
 
1106
1069
  Returns:
1107
1070
 
@@ -1116,29 +1079,34 @@ def load_datapackage(
1116
1079
  return obj
1117
1080
 
1118
1081
 
1119
- def simple_graph(data: dict, fs: Optional[FS] = None, **metadata):
1082
+ def simple_graph(data: dict, fs: Optional[AbstractFileSystem] = None, **metadata) -> Datapackage:
1120
1083
  """Easy creation of simple datapackages with only persistent vectors.
1121
1084
 
1122
- ``data`` is a dictionary with the form:
1085
+ Args:
1086
+ * data: is a dictionary.
1087
+ The data dictionary has the form::
1123
1088
 
1124
- ..code-block:: python
1089
+ {
1090
+ matrix_name: [
1091
+ (row_id, col_id, value, flip)
1092
+ ]
1093
+ }
1125
1094
 
1126
- matrix_name (str): [
1127
- (row id (int), col id (int), value (float), flip (bool, default False))
1128
- ]
1129
1095
 
1130
- ``fs`` is a filesystem.
1096
+ Where `row_id` and `col_id are an `int` s, value is a `float` and flip is a `bool` (False by default).
1131
1097
 
1132
- ``metadata`` are passed as kwargs to ``create_datapackage()``.
1098
+ * fs: is a filesystem.
1099
+ * metadata: are passed as kwargs to ``create_datapackage()``.
1133
1100
 
1134
- Returns the datapackage."""
1135
- dp = create_datapackage(fs=fs, **metadata)
1101
+ Returns:
1102
+ the datapackage.
1103
+
1104
+ """
1105
+ dp = create_datapackage(fs=fs or DictFS(), **metadata)
1136
1106
  for key, value in data.items():
1137
1107
  indices_array = np.array([row[:2] for row in value], dtype=INDICES_DTYPE)
1138
1108
  data_array = np.array([row[2] for row in value])
1139
- flip_array = np.array(
1140
- [row[3] if len(row) > 3 else False for row in value], dtype=bool
1141
- )
1109
+ flip_array = np.array([row[3] if len(row) > 3 else False for row in value], dtype=bool)
1142
1110
  dp.add_persistent_vector(
1143
1111
  matrix=key,
1144
1112
  data_array=data_array,
@@ -29,7 +29,8 @@ class ShapeMismatch(BrightwayProcessingError):
29
29
  class InvalidName(BrightwayProcessingError):
30
30
  """Name fails datapackage requirements:
31
31
 
32
- A short url-usable (and preferably human-readable) name of the package. This MUST be lower-case and contain only alphanumeric characters along with ".", "_" or "-" characters."""
32
+ A short url-usable (and preferably human-readable) name of the package. This MUST be lower-case and contain only alphanumeric characters along with ".", "_" or "-" characters.
33
+ """
33
34
 
34
35
  pass
35
36