Skip to content
Snippets Groups Projects
Commit a5e9a08d authored by Kathara Sasikumar's avatar Kathara Sasikumar
Browse files

New upstream version 0.3.0

parent 5f275e04
No related branches found
No related tags found
No related merge requests found
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
```python
Please add a code snippet here, that reproduces the problem completely
```
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here.
blank_issues_enabled: true
contact_links:
- name: Question
url: 'https://github.com/roman-right/lazy_model/discussions/new?category=question'
about: Ask a question about how to use Lazy Model using github discussions
- name: Feature Request
url: 'https://github.com/roman-right/lazy_model/discussions/new?category=feature-request'
about: >
If you think we should add a new feature to Lazy Model, please start a discussion, once it attracts
wider support, it can be migrated to an issue
\ No newline at end of file
name: Lint
on: [ pull_request ]
jobs:
lint:
strategy:
fail-fast: false
matrix:
python-version: [ 3.12 ]
os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: flake8 install
run: pip3 install flake8
- name: lint
run: flake8 lazy_model/ tests/
\ No newline at end of file
name: MyPy
on: [ pull_request ]
jobs:
mypy:
strategy:
fail-fast: false
matrix:
python-version: [ 3.12 ]
os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: mypy install
run: pip3 install mypy types-click types-toml
- name: mypy
run: mypy lazy_model/ --config-file pyproject.toml
name: Publish project
on:
push:
branches:
- main
jobs:
publish_project:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.12
- name: install poetry
uses: abatilo/actions-poetry@v2
with:
poetry-version: 1.2
- name: install dependencies
run: poetry install
- name: publish project
run: poetry publish --build --username __token__ --password ${{ secrets.PYPI_TOKEN }}
\ No newline at end of file
name: Tests
on: [ pull_request ]
jobs:
run-tests:
strategy:
fail-fast: false
matrix:
python-version: [ 3.8, 3.9, "3.10", 3.11, 3.12 ]
poetry-version: [ 1.2 ]
pydantic-version: [ 1.10.0, 2.10 ]
os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Run image
uses: abatilo/actions-poetry@v2
with:
poetry-version: ${{ matrix.poetry-version }}
- name: poetry install
run: poetry install
- name: pydantic install
run: poetry add pydantic==${{ matrix.pydantic-version }}
- name: run tests
run: poetry run pytest
config.cnf
*.pyc
*.iml
*/*.pytest*
.rnd
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
.static_storage/
.media/
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
### VirtualEnv template
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
.Python
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
pyvenv.cfg
.venv
pip-selfcheck.json
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
cmake-build-release/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
.idea
.pytest_cache
docs/api
docs/_rst
tags
tests/assets/tmp
src/api_files/storage_dir
docker-compose-aws.yml
tilt_modules
# Poetry stuff
poetry.lock
\ No newline at end of file
repos:
- repo: https://github.com/ambv/black
rev: 22.12.0
hooks:
- id: black
language_version: python3.12
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: mypy
exclude: ^tests/
Metadata-Version: 2.1
Name: lazy-model
Version: 0.2.0
Summary:
License: Apache-2.0
Author: Roman Right
Author-email: roman-right@protonmail.com
Requires-Python: >=3.7,<4.0
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Requires-Dist: pydantic (>=1.9.0)
Description-Content-Type: text/markdown
# Lazy parsing for Pydantic models
This library provides a lazy interface for parsing objects from dictionaries. During the parsing, it saves the raw data inside the object and parses each field on demand.
## Install
poetry
```shell
poetry add lazy-model
```
pip
```shell
pip install lazy-model
```
## Usage
```python
from lazy_model import LazyModel
from pydantic import validator
class Sample(LazyModel):
i: int
s: str
@validator("s")
def s_upper(cls, v):
return v.upper()
obj = Sample.lazy_parse({"i": "10", "s": "test"})
# at this point the data is stored in a raw format inside the object
print(obj.__dict__)
# >>> {'i': NAO, 's': NAO}
# NAO - Not An Object. It shows that the field was not parsed yet.
print(obj.s)
# >>> TEST
# Custom validator works during lazy parsing
print(obj.__dict__)
# >>> {'i': NAO, 's': 'TEST'}
# The `s` field was already parsed by this step
print(obj.i, type(obj.i))
# >>> 10 <class 'int'>
# It converted `10` from string to int based on the annotations
print(obj.__dict__)
# >>> {'i': 10, 's': 'TEST'}
# Everything was parsed
```
......@@ -38,18 +38,21 @@ class LazyModel(BaseModel):
return m
def _parse_value(self, name, value):
field_type = self.model_fields.get(name).annotation
field_type = self.__class__.model_fields.get(name).annotation
try:
value = TypeAdapter(field_type).validate_python(value)
except ValidationError as e:
if value is None and self.model_fields.get(name).required is False:
if (
value is None
and self.__class__.model_fields.get(name).required is False
):
value = None
else:
raise e
return value
def parse_store(self):
for name in self.model_fields:
for name in self.__class__.model_fields:
self.__getattribute__(name)
def _set_attr(self, name: str, value: Any) -> None:
......@@ -106,7 +109,7 @@ class LazyModel(BaseModel):
# For instances, use the object's __getattribute__ to prevent recursion
res = object.__getattribute__(self, item)
if res is NAO:
field_info = self.model_fields.get(item)
field_info = self.__class__.model_fields.get(item)
alias = field_info.alias or item
value = self._store.get(alias, NAO)
if value is NAO:
......
[tool.poetry]
name = "lazy-model"
version = "0.2.0"
version = "0.3.0"
description = ""
authors = ["Roman Right <roman-right@protonmail.com>"]
license = "Apache-2.0"
......@@ -9,7 +9,7 @@ readme = "README.md"
packages = [{include = "lazy_model"}]
[tool.poetry.dependencies]
python = ">=3.7,<4.0"
python = ">=3.8,<4.0"
pydantic = ">=1.9.0"
[tool.poetry.group.dev.dependencies]
......
# -*- coding: utf-8 -*-
from setuptools import setup
packages = \
['lazy_model', 'lazy_model.parser']
package_data = \
{'': ['*']}
install_requires = \
['pydantic>=1.9.0']
setup_kwargs = {
'name': 'lazy-model',
'version': '0.2.0',
'description': '',
'long_description': '# Lazy parsing for Pydantic models\n\nThis library provides a lazy interface for parsing objects from dictionaries. During the parsing, it saves the raw data inside the object and parses each field on demand.\n\n## Install\n\npoetry\n```shell\npoetry add lazy-model\n```\n\npip\n```shell\npip install lazy-model\n```\n\n## Usage\n\n```python\nfrom lazy_model import LazyModel\nfrom pydantic import validator\n\n\nclass Sample(LazyModel):\n i: int\n s: str\n\n @validator("s")\n def s_upper(cls, v):\n return v.upper()\n\n\nobj = Sample.lazy_parse({"i": "10", "s": "test"})\n\n# at this point the data is stored in a raw format inside the object\n\nprint(obj.__dict__)\n\n# >>> {\'i\': NAO, \'s\': NAO}\n\n# NAO - Not An Object. It shows that the field was not parsed yet.\n\nprint(obj.s)\n\n# >>> TEST\n\n# Custom validator works during lazy parsing\n\nprint(obj.__dict__)\n\n# >>> {\'i\': NAO, \'s\': \'TEST\'}\n\n# The `s` field was already parsed by this step\n\nprint(obj.i, type(obj.i))\n\n# >>> 10 <class \'int\'>\n\n# It converted `10` from string to int based on the annotations\n\nprint(obj.__dict__)\n\n# >>> {\'i\': 10, \'s\': \'TEST\'}\n\n# Everything was parsed\n```',
'author': 'Roman Right',
'author_email': 'roman-right@protonmail.com',
'maintainer': 'None',
'maintainer_email': 'None',
'url': 'None',
'packages': packages,
'package_data': package_data,
'install_requires': install_requires,
'python_requires': '>=3.7,<4.0',
}
setup(**setup_kwargs)
from typing import List
from pydantic import validator, root_validator, Field
from lazy_model.main import LazyModel
class Simple(LazyModel):
i: int
s: str
@validator("s")
def s_upper(cls, v):
return v.upper()
@root_validator(pre=True)
def check_card_number_omitted(cls, values):
"i" in values
return values
class Nested(LazyModel):
s: Simple
lst: List[Simple]
class Inherited(Simple):
f: float
class WithAlias(LazyModel):
i: int = Field(alias="_i_alias")
from lazy_model import LazyModel
from lazy_model.nao import NAO
from tests.models import Simple, Nested, Inherited, WithAlias
class TestParsing:
def test_simple_parse(self):
obj = Simple.lazy_parse({"i": "10", "s": "test"})
assert obj.__dict__["i"] == NAO
assert obj.__dict__["s"] == NAO
assert obj.i == 10
assert obj.s == "TEST"
def test_simple_parse_with_fields(self):
obj = Simple.lazy_parse({"i": "10", "s": "test"}, fields={"s"})
assert obj.__dict__["i"] == NAO
assert obj.__dict__["s"] == "TEST"
assert obj.i == 10
assert obj.s == "TEST"
def test_simple_parse_store(self):
obj = Simple.lazy_parse({"i": "10", "s": "test"})
obj.parse_store()
assert obj.__dict__ == {"i": 10, "s": "TEST"}
def test_nested_parse(self):
obj = Nested.lazy_parse(
{
"s": {"i": "10", "s": "test"},
"lst": [{"i": "10", "s": "test"}, {"i": "10", "s": "test"}],
}
)
assert obj.__dict__ == {"s": NAO, "lst": NAO}
assert obj.s == Simple(i=10, s="TEST")
assert obj.lst == [Simple(i=10, s="TEST"), Simple(i=10, s="TEST")]
def test_inheritance(self):
obj = Inherited.lazy_parse({"i": "10", "s": "test", "f": 1.23})
assert obj.__dict__ == {"i": NAO, "s": NAO, "f": NAO}
assert obj.i == 10
obj.parse_store()
assert obj.__dict__ == {"f": 1.23, "i": 10, "s": "TEST"}
def test_with_alias(self):
obj = WithAlias.lazy_parse({"_i_alias": 100})
assert obj.__dict__ == {"i": NAO}
assert obj.i == 100
def test_with_class(self):
class A(LazyModel):
def test_func(self):
print("test_func")
# Test
A.__getattribute__(A, "test_func")
A().__getattribute__("test_func")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment