o
    qoiq#                     @   s<  d Z ddlZddlZddlZddlZddlmZmZmZ ddl	m
Z
 ddlmZ ddlmZ ddlZeejejf Zeg Zejddd	ed
efddZded
ejfddZded
efddZded
efddZdeded
efddZded
efddZdeded
dfddZded
efddZ ej!G d d! d!Z"dS )"z.Manages import statements for code generation.    N)AnyDictUnion)logging)special_overrides)	namespace   )maxsizestmtreturnc                 C   sr   t j| }t|t jstd| d| t|jdkr!td|jd }t|t jt j	fs7t
d| d|S )z^Returns a LibCST node for an import statement.

  Args:
    stmt: String form of the input.
  zGot unexpected type z from    z"Expected only one line in {stmt!r}r   zUnexpected import z(, expected an Import or ImportFrom node.)csthelpersparse_template_statement
isinstanceSimpleStatementLine
ValueErrorlenbodyImport
ImportFrom	TypeError)r
   parsedresult r   V/home/ubuntu/.local/lib/python3.10/site-packages/fiddle/_src/codegen/import_manager.pyparse_import!   s   

r   nodec                 C   s&   t | jdkrtd|  | jd S )Nr   z7CST nodes with more than 1 name are not supported; got r   )r   namesr   r   r   r   r   _get_import_name_node4   s
   
r    c                 C   s(   t | }|jrt|jjS t|jS )zReturns the name for an import.

  For most imports, this is a single string for the Python accessible name. For
  dotted imports, like "import fiddle.tagging", it is the dotted name.

  Args:
    node: Any import.
  )r    asname_dummy_module_for_formattingcode_for_nodename)r   r$   r   r   r   get_import_name;   s   	r%   c                 C   s   t | ddd S )zReturns the name of an import taken up in the Python namespace.

  This is the same as get_import_name except for dotted imports like `foo.bar`,
  where it is the first name (`foo`).

  Args:
    node: Any import.
  .r   r   )r%   splitr   r   r   r   get_namespace_nameK   s   	r(   new_namec                 C   s*   t | }| j|jtt|dgdS )N)r!   )r   )r    with_changesr   AsNameName)r   r)   r$   r   r   r   change_aliasW   s   r-   c                 C   s:   t t| j}t| tjrt | j}| d| S |S )z6Returns the fully-qualified module name for an import.r&   )r"   r#   r    r$   r   r   r   module)r   name_str
module_strr   r   r   get_full_module_name]   s   r1   r$   import_stmtc                 C   s   t j| |d}t | | dS )a0  Registers an import alias.

  This function can be used to customize codegen, by changing references to a
  source module to a public module. For example, let's say you have a public API
  `foo/bar.py` which has a `from foo._src.bar import Baz` statement. When your
  Python code references `foo.bar.Baz`, the `inspect` module will see
  `foo._src.bar.Baz`, and so codegen will emit this name/import. To replace the
  private (in this example, _src) import with the public API, please use

  ```
  register_import_alias("foo._src.bar", "from foo import bar")
  ```

  This will make codegen to emit `from foo import bar` at the top, and use
  `bar.Baz` in generated fixtures. (You can also change the second argument to
  "import foo.bar" if you want it to use `foo.bar.Baz` in fixtures.)

  Typically this is called by extensions in `fiddle.extensions`.

  Args:
    name: Full module name to alias. Often, this is what can be found in
      `type(py_value).__module__.__name__`.
    import_stmt: Import statement for this module, which will be parsed by
      LibCST.
  )module_namemodule_import_aliasN)r   SpecialOverridesregister_special_override)r$   r2   import_overrider   r   r   register_import_aliash   s   r8   full_module_namec                 C   s:   d| v r|  dd\}}td| d| S td|  S )z4Makes an import statement from a string module name.r&   r   zfrom z import zimport )rsplitr   )r9   parent_namer$   r   r   r   _make_import   s   r<   c                   @   s~   e Zd ZU dZejed< eje	dZ
eeef ed< dedefddZd	edefd
dZdedefddZdd Zdd ZdS )ImportManagerz5Helper class to maintain a list of import statements.r   )default_factoryimports_by_full_namer
   r   c                 C   sd   t |tjrdS t|dd }t|}| j D ]}t||kr/t|dd |k  S qdS )a  Returns whether an import is compatible with existing ones.

    For example, if the current imports are,

    from foo import bar
    import foo.baz

    then 'import foo.bar' is fine, and 'from qux import foo' is not. As an edge
    case, 'from foo import foo' is also not okay, because this is trying to
    import 'foo.foo' as 'foo', which will cause problems when importing
    'foo.bar'.

    Args:
      stmt: Import statement being added.
    Fr&   r   N)r   r   r   r1   r'   r(   r?   values)selfr
   base_module_namenamespace_namer   r   r   r   _compatible_with_existing   s   z'ImportManager._compatible_with_existingr9   c                 C   s   t j|}|du rt|}nt|j}t|}|| jv r$t| j| S t	|}| 
|r.n|| jv rA| jj|dd}t||}n| j| || j|< t|S )a(  Adds an import given a module name.

    This is a slightly lower-level API than `add`; you should only use it if
    you don't have access to a function or class to pass to `add`.

    Args:
      full_module_name: String module name to try to import.

    Returns:
      Name for the imported module. This is usually the last name, possibly
      followed by a numeric suffix if necessary to disambiguate from other
      imports or variables. In a few cases where special import aliases are
      applied, then a name with a "." may be emitted.
    N )prefix)r   SPECIAL_OVERRIDES_MAPgetr<   r   r4   r1   r?   r%   r(   rD   r   get_new_namer-   add)rA   r9   r   rC   r)   r   r   r   add_by_name   s    





zImportManager.add_by_namevaluec                 C   sb   t |j}t|tjr|jjd |j }n|j}|dkr%t	
d| |S | |}| d| S )zAdds an import if it doesn't exist.

    This adds an import statement to this manager.

    Args:
      value: Function, class, or enum value.

    Returns:
      Relative-qualified name for the instance.
    r&   __main__z8%s's module is __main__, so an import couldn't be added.)inspect	getmodule__name__r   enumEnum	__class____qualname__r$   r   warningrK   )rA   rL   r3   value_qualnameimported_namer   r   r   rJ      s   
zImportManager.addc                 C   s   t t| j tdS ))Returns imports sorted lexicographically.)key)sorted	frozensetr?   r@   r1   rA   r   r   r   sorted_import_nodes   s   z!ImportManager.sorted_import_nodesc                 C   s   dd |   D S )rX   c                 S   s   g | ]	}t j|gd qS ))r   )r   r   ).0r2   r   r   r   
<listcomp>  s    z5ImportManager.sorted_import_lines.<locals>.<listcomp>)r]   r\   r   r   r   sorted_import_lines   s   z!ImportManager.sorted_import_linesN)rP   
__module__rT   __doc__r   	Namespace__annotations__dataclassesfielddictr?   r   str	AnyImportboolrD   rK   r   rJ   r]   r`   r   r   r   r   r=      s   
 
*r=   )#rb   re   rQ   	functoolsrN   typingr   r   r   abslr   fiddle._srcr   fiddle._src.codegenr   libcstr   r   r   ri   Moduler"   	lru_cacherh   r   ImportAliasr    r%   r(   r-   r1   r8   r<   	dataclassr=   r   r   r   r   <module>   s.   

 	