Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: forward refs #69

Merged
merged 3 commits into from Oct 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .coveragerc
Expand Up @@ -8,4 +8,5 @@ exclude_lines =
# Don't complain if non-runnable code isn't run:
pragma: no cover
except ImportError
if __name__ == .__main__.:
raise ImportError
if __name__ == .__main__.:
6 changes: 6 additions & 0 deletions CHANGELOG.md
@@ -1,4 +1,10 @@
# Changelog
## Pedantic 1.13.1
- fix `ImportError` of optional `multiprocess` package
- fix typo in `README.md`
- added more test for `@in_subprocess` decorator
- improve resolving of `typing.ForwardRef`s

## Pedantic 1.13.0
- added `GenericMixin`
- added `@in_subprocess` decorator
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -178,7 +178,7 @@ if __name__ == '__main__':
- [@validate](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/decorators/fn_deco_validate/fn_deco_validate.html)

## List of all mixins in this package
- [GenericMixin](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/mxins/generic_mixin.html)
- [GenericMixin](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/mixins/generic_mixin.html)

## Dependencies
There are no hard dependencies. But if you want to use some advanced features you need to install the following packages:
Expand Down
37 changes: 26 additions & 11 deletions docs/pedantic/decorators/cls_deco_frozen_dataclass.html
Expand Up @@ -29,8 +29,9 @@ <h1 class="title">Module <code>pedantic.decorators.cls_deco_frozen_dataclass</co
<pre><code class="python">import sys
from copy import deepcopy
from dataclasses import dataclass, fields
from typing import Type, TypeVar, Any, Union, Callable
from typing import Type, TypeVar, Any, Union, Callable, Dict

from pedantic.get_context import get_context
from pedantic.type_checking_logic.check_types import assert_value_matches_type

T = TypeVar(&#39;T&#39;)
Expand Down Expand Up @@ -116,32 +117,39 @@ <h1 class="title">Module <code>pedantic.decorators.cls_deco_frozen_dataclass</co
current_values = {field.name: deepcopy(getattr(self, field.name)) for field in fields(self)}
return cls_(**{**current_values, **kwargs})

def validate_types(self) -&gt; None:
def validate_types(self, *, _context: Dict[str, Type] = None) -&gt; None:
&#34;&#34;&#34;
Checks that all instance variable have the correct type.
Raises a [PedanticTypeCheckException] if at least one type is incorrect.
&#34;&#34;&#34;

props = fields(cls_)

if _context is None:
# method was called by user
_context = get_context(depth=2)

for field in props:
assert_value_matches_type(
value=getattr(self, field.name),
type_=field.type,
err=f&#39;In dataclass &#34;{cls_.__name__}&#34; in field &#34;{field.name}&#34;: &#39;,
type_vars={},
context=_context,
)

setattr(cls_, copy_with.__name__, copy_with)
setattr(cls_, deep_copy_with.__name__, deep_copy_with)
setattr(cls_, validate_types.__name__, validate_types)
methods_to_add = [copy_with, deep_copy_with, validate_types]

for method in methods_to_add:
setattr(cls_, method.__name__, method)

if type_safe:
old_post_init = getattr(cls_, &#39;__post_init__&#39;, lambda _: None)

def new_post_init(self) -&gt; None:
old_post_init(self)
self.validate_types()
context = get_context(3)
self.validate_types(_context=context)

setattr(cls_, &#39;__post_init__&#39;, new_post_init)

Expand Down Expand Up @@ -294,32 +302,39 @@ <h2 class="section-title" id="header-functions">Functions</h2>
current_values = {field.name: deepcopy(getattr(self, field.name)) for field in fields(self)}
return cls_(**{**current_values, **kwargs})

def validate_types(self) -&gt; None:
def validate_types(self, *, _context: Dict[str, Type] = None) -&gt; None:
&#34;&#34;&#34;
Checks that all instance variable have the correct type.
Raises a [PedanticTypeCheckException] if at least one type is incorrect.
&#34;&#34;&#34;

props = fields(cls_)

if _context is None:
# method was called by user
_context = get_context(depth=2)

for field in props:
assert_value_matches_type(
value=getattr(self, field.name),
type_=field.type,
err=f&#39;In dataclass &#34;{cls_.__name__}&#34; in field &#34;{field.name}&#34;: &#39;,
type_vars={},
context=_context,
)

setattr(cls_, copy_with.__name__, copy_with)
setattr(cls_, deep_copy_with.__name__, deep_copy_with)
setattr(cls_, validate_types.__name__, validate_types)
methods_to_add = [copy_with, deep_copy_with, validate_types]

for method in methods_to_add:
setattr(cls_, method.__name__, method)

if type_safe:
old_post_init = getattr(cls_, &#39;__post_init__&#39;, lambda _: None)

def new_post_init(self) -&gt; None:
old_post_init(self)
self.validate_types()
context = get_context(3)
self.validate_types(_context=context)

setattr(cls_, &#39;__post_init__&#39;, new_post_init)

Expand Down
15 changes: 13 additions & 2 deletions docs/pedantic/decorators/fn_deco_in_subprocess.html
Expand Up @@ -29,8 +29,13 @@ <h1 class="title">Module <code>pedantic.decorators.fn_deco_in_subprocess</code><
<pre><code class="python">import asyncio
import inspect
from functools import wraps
from multiprocess import Process, Queue
from typing import Callable, TypeVar, Any, Awaitable
from typing import Callable, TypeVar, Any, Awaitable, Type, Optional

try:
from multiprocess import Process, Queue
except ImportError:
Process: Optional[Type] = None
Queue: Optional[Type] = None

T = TypeVar(&#39;T&#39;)

Expand Down Expand Up @@ -99,6 +104,9 @@ <h1 class="title">Module <code>pedantic.decorators.fn_deco_in_subprocess</code><
84
&#34;&#34;&#34;

if Queue is None:
raise ImportError(&#39;You need to install the multiprocess package to use this: pip install multiprocess&#39;)

queue = Queue(maxsize=1) # a queue with items of type T
process = Process(target=_inner, args=(queue, func, *args), kwargs=kwargs)
process.start()
Expand Down Expand Up @@ -193,6 +201,9 @@ <h2 id="example">Example</h2>
84
&#34;&#34;&#34;

if Queue is None:
raise ImportError(&#39;You need to install the multiprocess package to use this: pip install multiprocess&#39;)

queue = Queue(maxsize=1) # a queue with items of type T
process = Process(target=_inner, args=(queue, func, *args), kwargs=kwargs)
process.start()
Expand Down
14 changes: 12 additions & 2 deletions docs/pedantic/decorators/fn_deco_pedantic.html
Expand Up @@ -73,11 +73,16 @@ <h1 class="title">Module <code>pedantic.decorators.fn_deco_pedantic</code></h1>
Use kwargs when you call function my_function. Args: (5, 4.0, &#39;hi&#39;)
&#34;&#34;&#34;

import sys
frame = sys._getframe(1) # get parent frame

def decorator(f: F) -&gt; F:
nonlocal frame

if not is_enabled():
return f

decorated_func = DecoratedFunction(func=f)
decorated_func = DecoratedFunction(func=f, context={**frame.f_globals, **frame.f_locals})

if decorated_func.docstring is not None and (require_docstring or len(decorated_func.docstring.params)) &gt; 0:
_check_docstring(decorated_func=decorated_func)
Expand Down Expand Up @@ -195,11 +200,16 @@ <h2 class="section-title" id="header-functions">Functions</h2>
Use kwargs when you call function my_function. Args: (5, 4.0, &#39;hi&#39;)
&#34;&#34;&#34;

import sys
frame = sys._getframe(1) # get parent frame

def decorator(f: F) -&gt; F:
nonlocal frame

if not is_enabled():
return f

decorated_func = DecoratedFunction(func=f)
decorated_func = DecoratedFunction(func=f, context={**frame.f_globals, **frame.f_locals})

if decorated_func.docstring is not None and (require_docstring or len(decorated_func.docstring.params)) &gt; 0:
_check_docstring(decorated_func=decorated_func)
Expand Down
98 changes: 98 additions & 0 deletions docs/pedantic/get_context.html
@@ -0,0 +1,98 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.10.0" />
<title>pedantic.get_context API documentation</title>
<meta name="description" content="" />
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>pedantic.get_context</code></h1>
</header>
<section id="section-intro">
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">import sys
from typing import Type, Dict


def get_context(depth: int = 1) -&gt; Dict[str, Type]:
&#34;&#34;&#34;
Get the context of a frame at the given depth of the current call stack.
See also: https://docs.python.org/3/library/sys.html#sys._getframe
&#34;&#34;&#34;

frame = sys._getframe(depth)
return {**frame.f_globals, **frame.f_locals}</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="pedantic.get_context.get_context"><code class="name flex">
<span>def <span class="ident">get_context</span></span>(<span>depth: int = 1) ‑> Dict[str, Type[+CT_co]]</span>
</code></dt>
<dd>
<div class="desc"><p>Get the context of a frame at the given depth of the current call stack.
See also: <a href="https://docs.python.org/3/library/sys.html#sys._getframe">https://docs.python.org/3/library/sys.html#sys._getframe</a></p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_context(depth: int = 1) -&gt; Dict[str, Type]:
&#34;&#34;&#34;
Get the context of a frame at the given depth of the current call stack.
See also: https://docs.python.org/3/library/sys.html#sys._getframe
&#34;&#34;&#34;

frame = sys._getframe(depth)
return {**frame.f_globals, **frame.f_locals}</code></pre>
</details>
</dd>
</dl>
</section>
<section>
</section>
</article>
<nav id="sidebar">
<h1>Index</h1>
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="pedantic" href="index.html">pedantic</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="pedantic.get_context.get_context" href="#pedantic.get_context.get_context">get_context</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>
7 changes: 7 additions & 0 deletions docs/pedantic/index.html
Expand Up @@ -38,6 +38,8 @@ <h1 class="title">Package <code>pedantic</code></h1>

from pedantic.mixins import GenericMixin

from pedantic.type_checking_logic import assert_value_matches_type, resolve_forward_ref

from pedantic.exceptions import NotImplementedException

from pedantic.env_var_logic import disable_pedantic, enable_pedantic, is_enabled
Expand Down Expand Up @@ -71,6 +73,10 @@ <h2 class="section-title" id="header-submodules">Sub-modules</h2>
<dd>
<div class="desc"></div>
</dd>
<dt><code class="name"><a title="pedantic.get_context" href="get_context.html">pedantic.get_context</a></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt><code class="name"><a title="pedantic.helper_methods" href="helper_methods.html">pedantic.helper_methods</a></code></dt>
<dd>
<div class="desc"></div>
Expand Down Expand Up @@ -113,6 +119,7 @@ <h1>Index</h1>
<li><code><a title="pedantic.env_var_logic" href="env_var_logic.html">pedantic.env_var_logic</a></code></li>
<li><code><a title="pedantic.examples" href="examples/index.html">pedantic.examples</a></code></li>
<li><code><a title="pedantic.exceptions" href="exceptions.html">pedantic.exceptions</a></code></li>
<li><code><a title="pedantic.get_context" href="get_context.html">pedantic.get_context</a></code></li>
<li><code><a title="pedantic.helper_methods" href="helper_methods.html">pedantic.helper_methods</a></code></li>
<li><code><a title="pedantic.mixins" href="mixins/index.html">pedantic.mixins</a></code></li>
<li><code><a title="pedantic.models" href="models/index.html">pedantic.models</a></code></li>
Expand Down