Python Load Tools Suite
Project description
Toadstool
Python load tools -- An opinionated solution for cleanly loading files directly into Python objects.
This library works by creating custom Python finders and loaders and injecting them into the Python meta search path.
The Python import system is explained in great detail here.
Consider a json file sample.json
{
"employee": {
"name": "sonoo",
"salary": 56000,
"married": true
},
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Save", "onclick": "SaveDoc()"}
]
}
}
}
can be loaded and used in the following way:
import toadstool
import sample
# Or import using ToadContext
from toadstool.utils.utils import ToadContext
from toadstool.loaders.json_loader import JsonLoader
with ToadContext(JsonLoader):
import sample
>>> sample.
sample.employee sample.json sample.menu
print(sample.menu)
> {'id': 'file', 'value': 'File', 'popup': {'menuitem': [{'value': 'New', 'onclick': 'CreateDoc()'}, {'value': 'Open', 'onclick': 'OpenDoc()'}, {'value': 'Save', 'onclick': 'SaveDoc()'}]}}
Requirements
Requires Python 3.10
Install
Basic Install
$ python3 -m pip install toadstool
Advanced Install
$ python3 -m pip install toadstool[options]
Where options are any of the following (or combinations thereof):
tomlFor.tomlsupport usingtomllibrary for Python versions prior toPython 3.11, which introducedtomllibinto the standard libraryyamlFor.yamlsupport usingPyYamlgqlFor.gqlor.graphqlquery support usinggraphql-corelibrary
Usage
To use this module, simply import toadstool before any import you wish to use the included loaders for. This can be at the top of your source file or just before any imports for the supported filetypes.
Loaders
GraphQL (.gql | .graphql)
Load graphql queries directly as graphql.language.ast.DocumentNode objects from the GraphQL Core Library. Allows direct importing of queries/mutations/subscriptions/fragments (aka GraphQL operations).
Example:
Given a GraphQL query file names queries.graphl or queries.gql with the following contents:
query HeroComparison($first: Int = 3) {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
friendsConnection(first: $first) {
totalCount
edges {
node {
name
}
}
}
}
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
then you can import the contents of the file either as a whole module:
import queries
print(queries.__dict__)
>
{'__name__': 'queries', '__doc__': None, '__package__': '', '__loader__': GqlImporter('queries.gql'), '__spec__': ModuleSpec(name='queries', loader=GqlImporter('queries.gql')), 'HeroComparison': DocumentNode, 'operations': {'HeroComparison': DocumentNode}, '__file__': 'queries.gql'}
or using specific query names:
from queries import HeroComparison
print(HeroComparison.definitions)
> (OperationDefinitionNode at 0:180, FragmentDefinitionNode at 182:339)
Also tracks all operations in a module dict as queries.operations
JSON (.json)
Loads JSON objects using builtin json library. The top-level JSON keys are stored as attirbutes for the module and the whole json converted dict is stored as imported_name.json For example, the following file sample.json
{
"employee": {
"name": "sonoo",
"salary": 56000,
"married": true
},
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Save", "onclick": "SaveDoc()"}
]
}
}
}
can be loaded and used in the following way:
import toadstool
import sample
>>> sample.
sample.employee sample.json sample.menu
print(sample.menu)
> {'id': 'file', 'value': 'File', 'popup': {'menuitem': [{'value': 'New', 'onclick': 'CreateDoc()'}, {'value': 'Open', 'onclick': 'OpenDoc()'}, {'value': 'Save', 'onclick': 'SaveDoc()'}]}}
You also have the json root object available at sample.json:
import toadstool
import sample
print(sample.json)
>{'employee': {'name': 'sonoo', 'salary': 56000, 'married': True}, 'menu': {'id': 'file', 'value': 'File', 'popup': {'menuitem': [{'value': 'New', 'onclick': 'CreateDoc()'}, {'value': 'Open', 'onclick': 'OpenDoc()'}, {'value': 'Save', 'onclick': 'SaveDoc()'}]}}}
TOML (.toml)
Loads TOML files such that each top-level table becomes an attribute of the imported module. Also loads the whole TOML file as a dictionary under the toml attirbute (which will overwrite any table from the file with the name toml as well). For example, if you have example.toml with the following contents:
[project]
name = 'Toadstool'
description = 'Python Load Tools Suite'
readme = 'README.md'
requires-python = "~=3.10"
license = { file = 'LICENSE' }
version = '0.1.0'
authors = [{ name = 'Andrés Alejos', email = 'acalejos@proton.me' }]
classifiers = [
'License :: OSI Approved :: MIT Licens',
'Programming Language :: Python :: 3',
'Topic :: Software Development',
'Topic :: Utilities',
]
keywords = ["import", "loader", "meta", "sys"]
urls = { Home = "https://github.com/acalejos/toadstool" }
[project.optional-dependencies]
gql = ['graphql_core>=3.2.3']
yaml = ['pyyaml >= 5.3.1']
toml = ['toml >= 0.10.2;python_version < "3.11"']
all = ['toadstool[gql]', 'toadstool[yaml]', 'toadstool[toml]']
[sample]
name = 'Sample'
[[Root]]
name = 'Root'
can be loaded and used in the following way:
import toadstool
import example
>>> example.
example.Root example.project example.sample example.toml
YAML (.yaml | .yml)
Loads a YAML file, assigning each top-level key as a module attribute (similar to the JSON loader). Also loads the whole YAML definition as a dict into the yaml attirbute. If the YAML file has more than 1 YAML definition (which is legal within the YAML definition), then only the yaml attribute is set, and is a list[dict]. Suppose you have a YAML file called sample.yaml with the following contents:
document: 1
name: 'erik'
then you could do the following:
import toadstool
import sample
>>> sample.
sample.document sample.name sample.yaml
and suppose you had a similar multifile YAML file:
document: 1
name: 'erik'
---
document: 2
name: 'config'
then you could do the following:
import toadstool
import sample
>>> sample.
sample.yaml
Config (.ini | .cfg | .config)
Uses the configparser package from the standard library to load the file and assign any top-level key to the module's attribute, and the entire ConfigParser object to the config attribute. Suppose you have the following config file sample.ini:
[http]
port=8080
username=httpuser
[https]
port=8043
username=httpsuser
[FTP]
port=8043
username=ftpuser
[database]
driverclass = com.mysql.jdbc.Driver
dbName = mydatabase
port = 3306
username = root
then you could do the following
import toadstool
import sample
>>> sample.
sample.http sample.https sample.FTP sample.database sample.config
CSV (.csv)
Loads a CSV file and loads the contents into rows and columns attributes as list[list[str]]. This will attempt to determine if the CSV contains a header as the first row, and will do the following according to that determination:
If Header:
- Creates attributes
named_rowsandnamed_columnsnamed_columns: Each column is adictkeyed on the column header and contains the whole columnnamed_rows: Each row contains adictwith each column entry keyed from its header to the value at that row.
- Populates each column name as an attribute name that will correspond to its respective column
- Populates an attribute
fieldnameswith the header values.
For any CSV:
- Creates a
rowsattirbute which contains alist[list[str]]with the CSV content rows (skips header if exists) - Creates a
columnsattirbute which contains alist[list[str]]with the CSV content columns
Suppose you have a file called sample.csv with the following contents:
Index,Customer Id,First Name
1,DD37Cf93aecA6Dc,Sheryl
2,1Ef7b82A4CAAD10,Preston
3,6F94879bDAfE5a6,Roy
4,5Cef8BFA16c5e3c,Linda
5,053d585Ab6b3159,Joanna
then you can do the following:
import toadstool
import sample
>>> sample.
sample.First_Name sample.named_rows sample.rows
sample.Index sample.columns sample.named_columns
sample.Customer_Id sample.fieldnames
ToadContext
Located in toadstool.utils.utils, ToadContext is a Context Manager to allow imports with toadstool loaders without permanently changing the sys.meta_path. Using this, you can import your files the same way as you would using import toadstool from within a context without having to actually import toadstool. You must explicitly pass loaders you wish to use as arguments.
This context manager does not yield anything, so the proper usage is:
with ToadContext(Loader | list(Loaders)):
import my_module
Limitations
Toadstool works by injecting the supported Loaders into the sys.meta_path during Toadstool's module init. The sys.meta_path is a list of class instances that contain the find_spec and exec_module methods. This occurs the first time that Toadstool is imported (upon init of Toadstool), after which toadstool is cached in the sys.modules data structure, which is what is checked when any module is referenced before deferring to the loaders in sys.meta_path and searching for the module. Therefore, after the first import toadstool that occurs per Python interpreter session, the module __init__.py might not be run again and thus if you do anything to alter the sys.meta_path after import toadstool then the Loaders may no longer be in the search path.
One possible solution is to use importlib.reload(toadstool) which reruns the module __init__.py code (only code not contained in if __name__ == '__main__' block).
Another possible solution is to use the ToadContext when you wish to import using the Toadstool Loaders, This could prove to be redundant, but it also ensures that the sys.meta_path is only altered while importing the modules that require toadstool and removes the Loaders upon existing the context.
Contributing
Feel free to open feature requests for new features or simply supported file extensions. If you feel so inclined, you can also open up a pull request to merge in your own loader. Creating a new loader is very straight forwards and can be accomplished by:
-
Creating a file in
toadstool/loaderswith the name{filetype}_loader.py -
Implementing a class that inherits from the
Loaderclass located intoadstool/loaders/base_loader. Your class must implement the following:- Add a class attribute
file_extsspecifying your file extension(s) either as astrfor a single extension orlist[str]for multiple extensions. For example:
class CsvLoader(Loader): file_exts="csv"
class GqlLoader(Loader): file_exts=["gql","graphql"]
- Define
def exec_module(self, module):where your functionality will be implemented. This will typically look like updating themodule.__dict__with values according to how you load the file
- Add a class attribute
-
If your loader has dependencies:
- Specify a new optional dependency in
pyrpoject.tomlunder[project.optional-dependencies] - Add your dependency to the
alloptional dependency by adding it astoadstool[your_dep]
- Specify a new optional dependency in
-
Add your loader to the list
all_loadersin the root__init__.pyfile as a 3-tuple with:- Path to your module in dotted notation
- Name of your loader class
- Name of dependency from
pyrpoject.tomlif it exists orNone
Refer to any of the existing loaders in toadstool/loaders for examples
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file toadstool-0.1.0.tar.gz.
File metadata
- Download URL: toadstool-0.1.0.tar.gz
- Upload date:
- Size: 13.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.22.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
266007a8fb3bd4e4cf1087791ff6c89b59754ba5d29fff4a8f272a92a124e515
|
|
| MD5 |
e90aa432bfda10f663f730bc72e4b209
|
|
| BLAKE2b-256 |
5b3bf0cca0a08446024ce9a60d4769dbf8fa95c7b9171dc6105ce040d7d4820f
|
File details
Details for the file toadstool-0.1.0-py3-none-any.whl.
File metadata
- Download URL: toadstool-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.22.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81cae6d802f5a2702de614e55888ea58f52000823d4befa220a376f74cdeda63
|
|
| MD5 |
40479747d6c425cf8a463a92e24ba5b6
|
|
| BLAKE2b-256 |
9a3ab1dbbdac4a35904ff79b127826c5145e2d68c9352d29e6d498971e44fcfe
|