sinamet 0.0.1__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 (39) hide show
  1. sinamet-0.0.1/.gitignore +15 -0
  2. sinamet-0.0.1/LICENSE +21 -0
  3. sinamet-0.0.1/PKG-INFO +138 -0
  4. sinamet-0.0.1/README.md +84 -0
  5. sinamet-0.0.1/pyproject.toml +59 -0
  6. sinamet-0.0.1/src/sinamet/__init__.py +17 -0
  7. sinamet-0.0.1/src/sinamet/core/config.py +154 -0
  8. sinamet-0.0.1/src/sinamet/core/mapper.py +273 -0
  9. sinamet-0.0.1/src/sinamet/errors.py +13 -0
  10. sinamet-0.0.1/src/sinamet/mtobjects/__init__.py +10 -0
  11. sinamet-0.0.1/src/sinamet/mtobjects/actor.py +32 -0
  12. sinamet-0.0.1/src/sinamet/mtobjects/dbobject.py +5 -0
  13. sinamet-0.0.1/src/sinamet/mtobjects/gateflow.py +62 -0
  14. sinamet-0.0.1/src/sinamet/mtobjects/mtobject.py +1276 -0
  15. sinamet-0.0.1/src/sinamet/mtobjects/pathflow.py +51 -0
  16. sinamet-0.0.1/src/sinamet/mtobjects/product.py +26 -0
  17. sinamet-0.0.1/src/sinamet/mtobjects/property.py +251 -0
  18. sinamet-0.0.1/src/sinamet/mtobjects/quantifiable.py +124 -0
  19. sinamet-0.0.1/src/sinamet/mtobjects/stock.py +48 -0
  20. sinamet-0.0.1/src/sinamet/mtobjects/territory.py +22 -0
  21. sinamet-0.0.1/src/sinamet/sidb/__init__.py +460 -0
  22. sinamet-0.0.1/src/sinamet/sidb/actor.py +392 -0
  23. sinamet-0.0.1/src/sinamet/sidb/gateflow.py +203 -0
  24. sinamet-0.0.1/src/sinamet/sidb/mtobject.py +590 -0
  25. sinamet-0.0.1/src/sinamet/sidb/pathflow.py +274 -0
  26. sinamet-0.0.1/src/sinamet/sidb/product.py +447 -0
  27. sinamet-0.0.1/src/sinamet/sidb/static.py +227 -0
  28. sinamet-0.0.1/src/sinamet/sidb/stock.py +119 -0
  29. sinamet-0.0.1/src/sinamet/sidb/territory.py +347 -0
  30. sinamet-0.0.1/src/sinamet/tools/__init__.py +0 -0
  31. sinamet-0.0.1/src/sinamet/tools/dataframe.py +65 -0
  32. sinamet-0.0.1/src/sinamet/tools/dictsyntax.py +31 -0
  33. sinamet-0.0.1/src/sinamet/tools/listmtq.py +239 -0
  34. sinamet-0.0.1/src/sinamet/tools/opestring.py +25 -0
  35. sinamet-0.0.1/src/sinamet/tools/path.py +38 -0
  36. sinamet-0.0.1/src/sinamet/tools/profile.py +287 -0
  37. sinamet-0.0.1/src/sinamet/tools/shell.py +10 -0
  38. sinamet-0.0.1/src/sinamet/tools/timext.py +138 -0
  39. sinamet-0.0.1/src/sinamet/tools/unitconverter.py +218 -0
@@ -0,0 +1,15 @@
1
+ dist/*
2
+ site/*
3
+ __pycache__
4
+
5
+ # Data files
6
+ *.xls
7
+ *.xlsx
8
+ *.csv
9
+
10
+ # Shapefiles
11
+ *.shp
12
+ *.dbf
13
+ *.shx
14
+ *.cpg
15
+ *.prj
sinamet-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Benoit Ribon, Dynartio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sinamet-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: sinamet
3
+ Version: 0.0.1
4
+ Summary: Structuring data to analyze territorial flows.
5
+ Project-URL: Documentation, https://docs.sinamet.tech
6
+ Project-URL: Source Code, https://github.com/dynartIO/sinamet
7
+ Author-email: Benoit Ribon <b.ribon@dynartio.com>, libadmin@sinamet.tech
8
+ License: MIT License
9
+
10
+ Copyright (c) 2025 Benoit Ribon, Dynartio
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Programming Language :: Python
32
+ Classifier: Programming Language :: Python :: 3.11
33
+ Classifier: Programming Language :: Python :: 3.12
34
+ Classifier: Programming Language :: Python :: 3.13
35
+ Requires-Python: >=3.11
36
+ Requires-Dist: anytree>=2.12.1
37
+ Requires-Dist: geoalchemy2>=0.16.0
38
+ Requires-Dist: numpy>=2.2.1
39
+ Requires-Dist: pandas>=2.2.3
40
+ Requires-Dist: psycopg2-binary>=2.9.10
41
+ Requires-Dist: shapely>=2.0.6
42
+ Requires-Dist: sqlalchemy>=2.0.36
43
+ Requires-Dist: unidecode>=1.3.8
44
+ Provides-Extra: demo
45
+ Requires-Dist: geopandas>=1.0.1; extra == 'demo'
46
+ Requires-Dist: openpyxl>=3.1.5; extra == 'demo'
47
+ Requires-Dist: xlrd>=2.0.1; extra == 'demo'
48
+ Provides-Extra: mkdocs
49
+ Requires-Dist: black; extra == 'mkdocs'
50
+ Requires-Dist: mkdocs; extra == 'mkdocs'
51
+ Requires-Dist: mkdocs-material; extra == 'mkdocs'
52
+ Requires-Dist: mkdocstrings[python]; extra == 'mkdocs'
53
+ Description-Content-Type: text/markdown
54
+
55
+ # 🇫🇷 Français
56
+
57
+ ## À propos de Sinamet
58
+
59
+ Sinamet est une bibliothèque Python spécialisée pour l'analyse des flux et stocks
60
+ territorialisés\*. Sinamet prend en charge la gestion d'une
61
+ base de données à travers un modèle de données spécifique. À travers ce modèle,
62
+ il est possible de structurer des données sur différentes thématiques,
63
+ tant qu'elles décrivent des flux territorialisés, par exemple :
64
+
65
+ * Des statistiques sur le transport de marchandises,
66
+ * Des déplacements domicile-travail,
67
+ * Des factures d’énergie et d'eau d'un patrimoine bâti,
68
+ * Des commandes d'un établissement de restauration collective,
69
+ * Des données sur la collecte de déchets,
70
+ * Et plein d'autres !
71
+
72
+ Sinamet embarque les fonctionnalités pour faciliter la navigation dans ces données :
73
+ organiser des hiérarchies d'objets, récupérer des flux ou des stocks associés
74
+ à un territoire ou un acteur, convertir des unités ou des nomenclatures,
75
+ suivre la temporalité et la source des données, ...
76
+
77
+ \*On parle également d'étudier le "métabolisme territorial".
78
+ Sinamet est d'ailleurs l'acronyme de "Système d'Information pour l'ANalyse du
79
+ MÉtabolisme des Territoires"
80
+
81
+ ## Documentation
82
+
83
+ Consultez la documentation en ligne: [https://docs.sinamet.tech](https://docs.sinamet.tech)
84
+
85
+ ## Contact
86
+
87
+ Des questions à propos de la bibliothèque ? Vous souhaitez être formé à
88
+ l'utilisation de l'outil ou vous souhaitez mettre un observatoire des flux sur
89
+ votre territoire ? Contactez l'équipe de développement : libadmin@sinamet.tech
90
+
91
+ ## Credit
92
+
93
+ Sinamet est une bibliothèque développée par [Dynartio](https://dynartio.com),
94
+ initiée grâce au financement de l'[ADEME](https://ademe.fr) et le soutien du
95
+ [Laboratoire LIVE (University or Strasbourg/CNRS)](https://live.unistra.fr)
96
+ et diffusée en Open Source grâce au soutien de la
97
+ [Nouvelle-Aquitaine Region](https://nouvelle-aquitaine.fr).
98
+
99
+ # 🇬🇧 English
100
+
101
+ ## About Sinamet
102
+
103
+ Sinamet is a Python library specialized in the analysis of territorialized
104
+ flows and stocks\*. Sinamet supports database management through a specific data
105
+ model. Using this model, it is possible to structure data of different themes,
106
+ as long as they describe territorialized flows, for example:
107
+
108
+ * Freight transport statistics,
109
+ * Commuting statistics,
110
+ * Energy and water bill for a building,
111
+ * Orders from a catering establishment,
112
+ * Waste collection data,
113
+ * And many more!
114
+
115
+ Sinamet includes functions to facilitate data navigation: organize object
116
+ hierarchies, retrieve flows or stocks associated with a territory or a player,
117
+ convert units or nomenclatures, track the temporality and source of data, etc.
118
+
119
+ \*We also talk about studying "territorial metabolism". Sinamet stands for
120
+ "Système d'Information pour l'ANalyse du MÉtabolisme des Territoires"
121
+
122
+ ## Documentation
123
+
124
+ View online documentation: [https://docs.sinamet.tech](https://docs.sinamet.tech)
125
+
126
+ ## Contact
127
+
128
+ Questions about the library ? Would you like to be trained to use the tool,
129
+ or would you like to set up an observatory of flows in your area ?
130
+ Contact the development team: libadmin@sinamet.tech
131
+
132
+ ## Credit
133
+
134
+ Sinamet is a library developed by [Dynartio](https://dynartio.com), initiated
135
+ thanks to funding from [ADEME](https://ademe.fr) and supports from
136
+ [Laboratoire LIVE (University or Strasbourg/CNRS)](https://live.unistra.fr), and
137
+ released as Open Source thanks to support from the
138
+ [Nouvelle-Aquitaine Region](https://nouvelle-aquitaine.fr).
@@ -0,0 +1,84 @@
1
+ # 🇫🇷 Français
2
+
3
+ ## À propos de Sinamet
4
+
5
+ Sinamet est une bibliothèque Python spécialisée pour l'analyse des flux et stocks
6
+ territorialisés\*. Sinamet prend en charge la gestion d'une
7
+ base de données à travers un modèle de données spécifique. À travers ce modèle,
8
+ il est possible de structurer des données sur différentes thématiques,
9
+ tant qu'elles décrivent des flux territorialisés, par exemple :
10
+
11
+ * Des statistiques sur le transport de marchandises,
12
+ * Des déplacements domicile-travail,
13
+ * Des factures d’énergie et d'eau d'un patrimoine bâti,
14
+ * Des commandes d'un établissement de restauration collective,
15
+ * Des données sur la collecte de déchets,
16
+ * Et plein d'autres !
17
+
18
+ Sinamet embarque les fonctionnalités pour faciliter la navigation dans ces données :
19
+ organiser des hiérarchies d'objets, récupérer des flux ou des stocks associés
20
+ à un territoire ou un acteur, convertir des unités ou des nomenclatures,
21
+ suivre la temporalité et la source des données, ...
22
+
23
+ \*On parle également d'étudier le "métabolisme territorial".
24
+ Sinamet est d'ailleurs l'acronyme de "Système d'Information pour l'ANalyse du
25
+ MÉtabolisme des Territoires"
26
+
27
+ ## Documentation
28
+
29
+ Consultez la documentation en ligne: [https://docs.sinamet.tech](https://docs.sinamet.tech)
30
+
31
+ ## Contact
32
+
33
+ Des questions à propos de la bibliothèque ? Vous souhaitez être formé à
34
+ l'utilisation de l'outil ou vous souhaitez mettre un observatoire des flux sur
35
+ votre territoire ? Contactez l'équipe de développement : libadmin@sinamet.tech
36
+
37
+ ## Credit
38
+
39
+ Sinamet est une bibliothèque développée par [Dynartio](https://dynartio.com),
40
+ initiée grâce au financement de l'[ADEME](https://ademe.fr) et le soutien du
41
+ [Laboratoire LIVE (University or Strasbourg/CNRS)](https://live.unistra.fr)
42
+ et diffusée en Open Source grâce au soutien de la
43
+ [Nouvelle-Aquitaine Region](https://nouvelle-aquitaine.fr).
44
+
45
+ # 🇬🇧 English
46
+
47
+ ## About Sinamet
48
+
49
+ Sinamet is a Python library specialized in the analysis of territorialized
50
+ flows and stocks\*. Sinamet supports database management through a specific data
51
+ model. Using this model, it is possible to structure data of different themes,
52
+ as long as they describe territorialized flows, for example:
53
+
54
+ * Freight transport statistics,
55
+ * Commuting statistics,
56
+ * Energy and water bill for a building,
57
+ * Orders from a catering establishment,
58
+ * Waste collection data,
59
+ * And many more!
60
+
61
+ Sinamet includes functions to facilitate data navigation: organize object
62
+ hierarchies, retrieve flows or stocks associated with a territory or a player,
63
+ convert units or nomenclatures, track the temporality and source of data, etc.
64
+
65
+ \*We also talk about studying "territorial metabolism". Sinamet stands for
66
+ "Système d'Information pour l'ANalyse du MÉtabolisme des Territoires"
67
+
68
+ ## Documentation
69
+
70
+ View online documentation: [https://docs.sinamet.tech](https://docs.sinamet.tech)
71
+
72
+ ## Contact
73
+
74
+ Questions about the library ? Would you like to be trained to use the tool,
75
+ or would you like to set up an observatory of flows in your area ?
76
+ Contact the development team: libadmin@sinamet.tech
77
+
78
+ ## Credit
79
+
80
+ Sinamet is a library developed by [Dynartio](https://dynartio.com), initiated
81
+ thanks to funding from [ADEME](https://ademe.fr) and supports from
82
+ [Laboratoire LIVE (University or Strasbourg/CNRS)](https://live.unistra.fr), and
83
+ released as Open Source thanks to support from the
84
+ [Nouvelle-Aquitaine Region](https://nouvelle-aquitaine.fr).
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "sinamet"
7
+ description = "Structuring data to analyze territorial flows."
8
+ readme = "README.md"
9
+ authors = [
10
+ {name = "Benoit Ribon", email = "b.ribon@dynartio.com"},
11
+ {email = "libadmin@sinamet.tech"},
12
+ ]
13
+ license = {file = "LICENSE"}
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ ]
21
+ requires-python = ">=3.11"
22
+ dependencies = [
23
+ "anytree>=2.12.1",
24
+ "GeoAlchemy2>=0.16.0",
25
+ "numpy>=2.2.1",
26
+ "pandas>=2.2.3",
27
+ "psycopg2-binary>=2.9.10",
28
+ "shapely>=2.0.6",
29
+ "SQLAlchemy>=2.0.36",
30
+ "Unidecode>=1.3.8",
31
+ ]
32
+ dynamic = ["version"]
33
+
34
+ [project.urls]
35
+ Documentation = "https://docs.sinamet.tech"
36
+ "Source Code" = "https://github.com/dynartIO/sinamet"
37
+
38
+ [project.optional-dependencies]
39
+ demo = [
40
+ "geopandas>=1.0.1",
41
+ "xlrd>=2.0.1",
42
+ "openpyxl>=3.1.5",
43
+ ]
44
+
45
+ mkdocs = [
46
+ "mkdocs",
47
+ "mkdocstrings[python]",
48
+ "mkdocs-material",
49
+ "black",
50
+ ]
51
+
52
+ [tool.hatch.version]
53
+ path = "src/sinamet/__init__.py"
54
+
55
+ [tool.hatch.build.targets.sdist]
56
+ exclude = [
57
+ "docs/",
58
+ "mkdocs.yml",
59
+ ]
@@ -0,0 +1,17 @@
1
+ from .core.mapper import Mapper
2
+ from .core.mapper import MapperError
3
+
4
+ from .core.config import config
5
+
6
+ from .sidb import Sidb
7
+
8
+ from .mtobjects import MTObject
9
+ from .mtobjects import Territory
10
+ from .mtobjects import Actor
11
+ from .mtobjects import Product
12
+ from .mtobjects import Pathflow
13
+ from .mtobjects import Gateflow
14
+ from .mtobjects import Stock
15
+ from .mtobjects import Property
16
+
17
+ __version__ = '0.0.1'
@@ -0,0 +1,154 @@
1
+ import os
2
+
3
+ from pathlib import Path
4
+
5
+ import tomllib
6
+
7
+ from typing import Any
8
+
9
+
10
+ class SinametConfig:
11
+ def __init__(self):
12
+ self.current_environref: str = "_"
13
+ self.configs: dict[str, dict[str, str]] = {}
14
+ self.config_file: dict[str, Any] = self.init_config_file()
15
+
16
+ def __getitem__(self, key: str) -> str:
17
+ try:
18
+ return self.configs[self.current_environref][key]
19
+ except KeyError:
20
+ return self.configs["_"][key]
21
+
22
+ def __setitem__(self, key: str, value: str):
23
+ if self.current_environref not in self.configs:
24
+ self.configs[self.current_environref] = {}
25
+ self.configs[self.current_environref][key] = value
26
+
27
+ def __contains__(self, item: str):
28
+ if self.current_environref not in self.configs:
29
+ return False
30
+ return item in self.configs[self.current_environref]
31
+
32
+ def init_key(self,
33
+ key: str,
34
+ value: str | None = None,
35
+ environref: str | None = None,
36
+ verbose: bool = False) -> str | None:
37
+ if not environref:
38
+ environref = self.current_environref
39
+ if environref not in self.configs:
40
+ self.configs[environref] = {}
41
+ if value is None:
42
+ value = self.find_key(key, environref, verbose=verbose)
43
+ if value or not self.configs[environref].get(key):
44
+ self.configs[environref][key] = value
45
+ return value
46
+
47
+ def init_db(self,
48
+ environref: str | None = None,
49
+ verbose: bool = False,
50
+ **kwargs) -> None:
51
+ if not environref:
52
+ environref = self.current_environref
53
+ if environref not in self.configs:
54
+ self.configs[environref] = {}
55
+
56
+ coredb_path = self.find_key('COREDB_PATH', environref, kwargs, verbose)
57
+ if coredb_path:
58
+ if verbose:
59
+ print(f"COREDB_PATH='{coredb_path}'")
60
+ self.configs[environref]['COREDB_PATH'] = coredb_path
61
+ return coredb_path
62
+
63
+ keys = [
64
+ "COREDB_NAME",
65
+ "COREDB_USER",
66
+ "COREDB_PASS",
67
+ "COREDB_HOST",
68
+ "COREDB_PORT",
69
+ ]
70
+ for key in keys:
71
+ value = self.find_key(key, environref, kwargs, verbose)
72
+ if value is not None:
73
+ self.configs[environref][key] = value
74
+ if not self.configs[environref].get(key):
75
+ raise KeyError(f"Could not find a value for {key}.")
76
+
77
+ coredb_path = ("postgresql://"
78
+ f"{self.configs[environref]['COREDB_USER']}"
79
+ f":{self.configs[environref]['COREDB_PASS']}"
80
+ f"@{self.configs[environref]['COREDB_HOST']}"
81
+ f":{self.configs[environref]['COREDB_PORT']}"
82
+ f"/{self.configs[environref]['COREDB_NAME']}")
83
+ self.configs[environref]["COREDB_PATH"] = coredb_path
84
+
85
+ if verbose:
86
+ print(f"COREDB_PATH='{coredb_path}'")
87
+ return coredb_path
88
+
89
+ def find_key(self,
90
+ key: str,
91
+ environref: str,
92
+ kwargs: dict = {},
93
+ verbose: bool = False
94
+ ) -> str | None:
95
+ """
96
+ Ordre de priorité de recherche:
97
+ - kwargs -> sans prefix ET sans environref
98
+ - os.environ -> avec prefix ET avec ou sans environref
99
+ - config file -> avec ou sans environref
100
+ """
101
+ if verbose:
102
+ print(f"find_key: {key=}")
103
+ print(">>>>> ", end="")
104
+
105
+ # Search in kwargs
106
+ if value := kwargs.get(key):
107
+ if verbose:
108
+ print(f"in kwargs: {key}='{value}'")
109
+ return value
110
+
111
+ # Search in environment variables
112
+ if value := os.getenv(f"SINAMET_{key}_{environref}"):
113
+ if verbose:
114
+ print(f"in os.environ: SINAMET_{key}_{environref}='{value}'")
115
+ return value
116
+ if value := os.getenv(f"SINAMET_{key}"):
117
+ if verbose:
118
+ print(f"in os.environ: SINAMET_{key}='{value}'")
119
+ return value
120
+
121
+ # Search in config file
122
+ current_env = os.getenv('SINAMET_CURRENT_ENV') or self.config_file.get('current_env')
123
+ if current_env in self.config_file:
124
+ if (value := self.config_file[current_env].get(key)):
125
+ if verbose:
126
+ print(f"in config file in {current_env} table: {key}='{value}'")
127
+ return value
128
+ if value := self.config_file.get(key):
129
+ if verbose:
130
+ print(f"in config file: {key}='{value}'")
131
+ return value
132
+ if verbose:
133
+ print(f"Key '{key}' not found.")
134
+
135
+ @staticmethod
136
+ def init_config_file() -> dict[str, Any]:
137
+ """
138
+ Note: FutureDev
139
+ Envisager une recherche avec des chemins absolus ou $PYTHONPATH.
140
+ """
141
+ path = Path.cwd()
142
+ while path != path.parent:
143
+ config_path = path / 'sinamet_config.toml'
144
+ path = path.parent
145
+
146
+ if not config_path.is_file() or not os.access(config_path, os.R_OK):
147
+ continue
148
+
149
+ with open(config_path, 'rb') as f:
150
+ return tomllib.load(f)
151
+ return {}
152
+
153
+
154
+ config = SinametConfig()