# Create delegated pyi


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Setup

## Basics

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/py2pyi.py#L15"
target="_blank" style="float:right; font-size:smaller">source</a>

### imp_mod

``` python

def imp_mod(
    module_path, package:NoneType=None
):

```

*Import dynamically the module referenced in `fn`*

``` python
fn = Path('test_py2pyi.py')
```

``` python
mod = imp_mod(fn)
a = mod.A()
a.h()
```

    1

``` python
tree = _get_tree(mod)
```

------------------------------------------------------------------------

### AST.\_\_repr\_\_

``` python

def __repr__(
    
):

```

``` python
# for o in enumerate(tree.body): print(o)
```

``` python
node = tree.body[4]
node
```

``` python
def f(a: int, b: str='a') -> str:
    """I am f"""
    return 1
```

``` python
isinstance(node, functypes)
```

    True

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/py2pyi.py#L50"
target="_blank" style="float:right; font-size:smaller">source</a>

### has_deco

``` python

def has_deco(
    node:Union, name:str
)->bool:

```

*Check if a function node `node` has a decorator named `name`*

``` python
nm = 'delegates'
has_deco(node, nm)
```

    False

``` python
node = tree.body[5]
node
```

``` python
@delegates(f)
def g(c, d: X, **kwargs) -> str:
    """I am g"""
    return 2
```

``` python
has_deco(node, nm)
```

    True

## Function processing

``` python
def _proc_body   (node, mod): print('_proc_body', type(node))
def _proc_func   (node, mod): print('_proc_func', type(node))
def _proc_class  (node, mod): print('_proc_class', type(node))
def _proc_patched(node, mod): print('_proc_patched', type(node))
```

``` python
parent_node = copy.deepcopy(tree.body[7])
patched_node = copy.deepcopy(tree.body[10])
test_is(has_deco(patched_node, "patch"), True)
test_eq(str(patched_node.args.args[0].annotation), parent_node.name)

_clean_patched_node(patched_node)
test_is(has_deco(patched_node, "patch"), False)
test_eq(patched_node.args.args[0].annotation, None)
```

``` python
empty_cls1, empty_cls2, empty_cls3 = ast.parse('''
class A: 
    """An empty class."""
class B: 
    pass
class C: 
    ...
''').body

test_is(_is_empty_class(empty_cls1), True)
test_is(_is_empty_class(empty_cls2), True)
test_is(_is_empty_class(empty_cls3), True)

non_empty_cls, empty_func = ast.parse('''
class A: 
    a = 1
def f():
    ...
''').body
test_is(_is_empty_class(non_empty_cls), False)
test_is(_is_empty_class(empty_func), False)
```

``` python
# we could have reused `parent_node` and `patched_node` from the previous cells.
# copying them here allows us to run this cell multiple times which makes it a little easier to write tests.

parent_node = copy.deepcopy(tree.body[7])
patched_node = copy.deepcopy(tree.body[11])
test_eq(len(parent_node.body),1)
_add_patched_node_to_parent(patched_node, parent_node)
test_eq(len(parent_node.body),2)
test_eq(parent_node.body[-1], patched_node)

# patched node replaces an existing class method (A.h)
patched_h_node = ast.parse("""
@patch
def h(self: A, *args, **kwargs):
    ...
""", mode='single').body[0]

_add_patched_node_to_parent(patched_h_node, parent_node)
test_eq(len(parent_node.body), 2)
test_eq(parent_node.body[0], patched_h_node)

# patched node is added to an empty class
empty_cls, patched_node = ast.parse('''
class Z: 
    """An empty class."""

@patch
def a(self: Z, *args, **kwargs):
    ...
''').body

test_eq(len(empty_cls.body), 1)
test_ne(empty_cls.body[0], patched_node)
_add_patched_node_to_parent(patched_node, empty_cls)
test_eq(len(empty_cls.body), 1)
test_eq(empty_cls.body[0], patched_node)
```

``` python
raw_tree = _get_tree(mod)
processed_tree = _proc_mod(mod)
n_raw_tree_nodes = len(raw_tree.body)
# mod contains 3 patch methods so our processed_tree should have 3 less nodes 
test_eq(len(processed_tree.body), n_raw_tree_nodes-3)
```

    _proc_class <class 'ast.ClassDef'>
    _proc_body <class 'ast.FunctionDef'>
    _proc_func <class 'ast.FunctionDef'>
    _proc_body <class 'ast.FunctionDef'>
    _proc_class <class 'ast.ClassDef'>
    _proc_class <class 'ast.ClassDef'>
    _proc_patched <class 'ast.FunctionDef'>
    _proc_patched <class 'ast.FunctionDef'>
    _proc_body <class 'ast.FunctionDef'>

``` python
_proc_mod(mod);
```

    _proc_class <class 'ast.ClassDef'>
    _proc_body <class 'ast.FunctionDef'>
    _proc_func <class 'ast.FunctionDef'>
    _proc_body <class 'ast.FunctionDef'>
    _proc_class <class 'ast.ClassDef'>
    _proc_class <class 'ast.ClassDef'>
    _proc_patched <class 'ast.FunctionDef'>
    _proc_patched <class 'ast.FunctionDef'>
    _proc_body <class 'ast.FunctionDef'>

``` python
node.name
```

    'g'

``` python
sym = getattr(mod, node.name)
sym
```

    <function test_py2pyi.g(c, d: test_py2pyi.X, *, b: str = 'a') -> str>

``` python
sig = signature(sym)
print(sig)
```

    (c, d: test_py2pyi.X, *, b: str = 'a') -> str

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L377"
target="_blank" style="float:right; font-size:smaller">source</a>

### sig2str

``` python

def sig2str(
    sig
):

```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/py2pyi.py#L139"
target="_blank" style="float:right; font-size:smaller">source</a>

### ast_args

``` python

def ast_args(
    func
):

```

``` python
newargs = ast_args(sym)
newargs
```

``` python
c, d: test_py2pyi.X, *, b: str='a'
```

``` python
node.args
```

``` python
c, d: X, **kwargs
```

``` python
node.args = newargs
node
```

``` python
@delegates(f)
def g(c, d: test_py2pyi.X, *, b: str='a') -> str:
    """I am g"""
    return 2
```

``` python
_body_ellip(node)
node
```

``` python
@delegates(f)
def g(c, d: test_py2pyi.X, *, b: str='a') -> str:
    """I am g"""
    ...
```

``` python
tree = _get_tree(mod)
node = tree.body[5]
node
```

``` python
@delegates(f)
def g(c, d: X, **kwargs) -> str:
    """I am g"""
    return 2
```

``` python
_update_func(node, sym)
node
```

``` python
def g(c, d: X, *, b: str='a') -> str:
    """I am g"""
    ...
```

``` python
tree = _proc_mod(mod)
tree.body[5]
```

    _proc_class <class 'ast.ClassDef'>
    _proc_class <class 'ast.ClassDef'>
    _proc_class <class 'ast.ClassDef'>
    _proc_patched <class 'ast.FunctionDef'>
    _proc_patched <class 'ast.FunctionDef'>

``` python
def g(c, d: X, *, b: str='a') -> str:
    """I am g"""
    ...
```

## Patch

``` python
tree = _get_tree(mod)
node = tree.body[9]
node
```

``` python
@patch
@delegates(j)
def k(self: (A, B), b: bool=False, **kwargs):
    return 1
```

``` python
ann = node.args.args[0].annotation
```

``` python
if hasattr(ann, 'elts'): ann = ann.elts[0]
```

``` python
nm = ann.id
nm
```

    'A'

``` python
cls = getattr(mod, nm)
sym = getattr(cls, node.name)
```

``` python
sig2str(signature(sym))
```

    "(self: (test_py2pyi.A, test_py2pyi.B), b: bool = False, *, d: str = 'a')"

``` python
_update_func(node, sym)
```

``` python
node
```

``` python
@patch
def k(self: (A, B), b: bool=False, *, d: str='a'):
    ...
```

``` python
tree = _get_tree(mod)
tree.body[9]
```

``` python
@patch
@delegates(j)
def k(self: (A, B), b: bool=False, **kwargs):
    return 1
```

## Class and file

``` python
tree = _get_tree(mod)
node = tree.body[7]
node
```

``` python
class A:

    @delegates(j)
    def h(self, b: bool=False, **kwargs):
        a = 1
        return a
```

``` python
node.body
```

    [@delegates(j)
     def h(self, b: bool=False, **kwargs):
         a = 1
         return a]

``` python
tree = _proc_mod(mod)
tree.body[7]
```

``` python
class A:

    def h(self, b: bool=False, *, d: str='a'):
        ...

    def k(self, b: bool=False, *, d: str='a'):
        ...

    def m(self, b: bool=False, *, d: str='a'):
        ...

    def n(self, b: bool=False, **kwargs):
        """No delegates here mmm'k?"""
        ...
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/py2pyi.py#L187"
target="_blank" style="float:right; font-size:smaller">source</a>

### create_pyi

``` python

def create_pyi(
    fn, package:NoneType=None
):

```

*Convert `fname.py` to `fname.pyi` by removing function bodies and
expanding [`delegates`](https://fastcore.fast.ai/meta.html#delegates)
kwargs*

``` python
create_pyi(fn)
```

``` python
# fn = Path('/Users/jhoward/git/fastcore/fastcore/docments.py')
# create_pyi(fn, 'fastcore')
```

## Script

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/py2pyi.py#L200"
target="_blank" style="float:right; font-size:smaller">source</a>

### py2pyi

``` python

def py2pyi(
    fname:str, # The file name to convert
    package:str=None, # The parent package
):

```

*Convert `fname.py` to `fname.pyi` by removing function bodies and
expanding [`delegates`](https://fastcore.fast.ai/meta.html#delegates)
kwargs*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/py2pyi.py#L208"
target="_blank" style="float:right; font-size:smaller">source</a>

### replace_wildcards

``` python

def replace_wildcards(
    path:str, # Path to the Python file to process
):

```

*Expand wildcard imports in the specified Python file.*
