o
    qoi;                     @   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m	Z	 ddl
mZ ddl
mZ ddl
mZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlmZ ejddddZejddddZejdg ddZ ejdg ddZ!ejdg ddZ"ej#ddddZ$dd Z%dej&fd d!Z'd"e	e( d#ee( fd$d%Z)d"e	e( fd&d'Z*dej&fd(d)Z+	d5dej&d*efd+d,Z,d-d. Z-d#e.fd/d0Z/		d6d1ee d2eej0 d#ej&fd3d4Z1dS )7a  Legacy API to use command line flags with Fiddle Buildables.

NOTE: Deprecation: These are legacy APIs. Please refer to the new API usage in
flags.py and find the documentation in flags_code_lab.md.

While it's generally better to check in the full configuration into a source-
control system, there are some instances (e.g. hyperparameter sweeps) where it's
most effective to set initialization parameters (hyperparameters) from command
line flags. This library exposes functions you can call within your program.

NOTE: The flags aren't applied in the order they are passed in on the command
  line. The order followed is:
  - all fiddlers are applied first, followed by
  - all tags, followed by
  - all overrides.

## Quickstart

> Note: although different bits of functionality are designed to be usable
> piecemeal, this is the fastest way to get all flag-integrated features.

First, some definitions:

 - *A module for base configurations*: So that libraries can be easily reused
   with or without fiddle, code to define fiddle configurations are often put
   in a separate package / module. Most commonly, there is one function (called
   `default` by convention)
 - *Fiddlers*: A fiddler is a function that takes a `fdl.Buildable` and performs
   arbitrary mutations. These are commonly also grouped into the same module
   that defines the base configuration(s).
 - *Overrides*: Overrides are command line options that set values to
   parameters within the (nested) fiddle tree.

The simplest way to use the fiddle support for absl flags is to pass
`flags_parser=absl_flags.flags_parser` when calling `app.run`. Then, simply call
`cfg = absl_flags.create_buildable_from_flags(configs)` where `configs` is a
module for base configurations and fiddlers.

For example:

```py
## ---- config.py ----
import fiddle as fdl

def baseline() -> fdl.Config:  # A base configuration.
  my_model = fdl.Config(MyModel)
  my_optimizer = fdl.Config(MyOptimizer)
  return fdl.Config(MyTrainer, optimizer=my_optimizer, model=my_model)

def set_dtypes_to_bfloat_16(cfg: fdl.Config):  # A fiddler.
  cfg.model.dtype = 'bfloat16'  # Arbitrary python.

##  ---- main.py ----
from absl import app
import fiddle as fdl
from fiddle import absl_flags
import config

def main(argv):
  cfg = absl_flags.create_buildable_from_flags(configs)
  trainer = fdl.build(cfg)
  trainer.train()

if __name__ == '__main__':
  app.run(main, flags_parser=absl_flags.flags_parser)
```

## Overrides-only

If you simply want to use command line flags to override values in a config
built through arbitrary python, follow these steps:

 1. *main*: Pass `fdl.absl_flags.flags_parser` when calling `absl.app.run`. For
    example, your `app.run` call should look like the following:
        `app.run(my_main_function, flags_parser=fiddle.absl_flags.flags_parser)`
 2. *Augment a config*: Somewhere in your program, call
    `fdl.absl_flags.apply_overrides_to(cfg)` to apply settings defined on the
    command line to `cfg`. (This is often best done right before the call to
    `fdl.build(cfg)`.)
 3. Set values on the command line: `--fdl.my.property=3 --fdl.other.setting=5`

### Configuring multiple fiddle objects

If you have multiple fiddle objects that you'd like to configure on the command
line, create an empty top-level config and use that. Example:

```
my_program_part_1 = fdl.Config(...)
my_program_part_2 = fdl.Config(...)

top_level = fiddle.experimental.DictConfig(part1=my_program_part_1)
top_level.part2 = my_program_part_2
fdl.absl_flags.apply_overrides_to(top_level)

run_part_1(fdl.build(my_program_part_1))
run_part_2(fdl.build(my_program_part_2))
```

## Acknowledgements

This implementation has drawn inspiration from multiple sources, including
[T5X](https://github.com/google-research/t5x), among others. Thank you!
    N)AnyListOptionalSequence)app)flags)logging)epath)printing)	selectors)config)tagging)utils)auto_config)serialization
fdl_configz9The name of a function to construct a base Fiddle config.)defaulthelpfdl_config_filezNThe path to a file containing a serialized base Fiddle config in JSON format. fiddlerzCThe name of a fiddler. Fiddlers are functions that modify a config.fdl_setzYPer-parameter configuration settings. Typically accessed via the alias --fdl.foo.bar=123.fdl_tags_setzTFiddle tags setting, by name. Typically accessed via the alias --fdl_tag.foo.bar=123fdl_helpFz\Print out Fiddle-specific help (including information on any loaded configuration) and exit.c                  O   s   t | i |dtji d S )Nfile)printsysstderr)argskwargs r   W/home/ubuntu/.local/lib/python3.10/site-packages/fiddle/_src/absl_flags/legacy_flags.py_print_stderr   s   r!   cfgc                 C   s   t jD ]}t| | qdS )z[DEPRECATED] Applies all command line flags to `cfg`.

  Deprecation: This is a legacy API. Please refer to the new API usage in
  flags.py and find the documentation in flags_code_lab.md.

  Args:
    cfg: The configuration to apply overrides to.
  N)_FDL_SETvaluer   	set_value)r"   flagr   r   r    apply_overrides_to   s   
	r'   r   returnc                 C   s    dt dt fdd}tt|| S )a   Rewrites short-form Fiddle flags.

  There are two main rewrites:

    * `--fdl.NAME=VALUE` to `--fdl_set=NAME = VALUE`.
    * `--fdl_tag.NAME=VALUE` to `--fdl_tags_set=NAME = VALUE`.

  Args:
    args: Command-line args.

  Returns:
    Rewritten args.
  argr(   c                 S   s   |  ds
|  drWd| vr"| jdddd }td| d	|  d
|  dr*d}n|  dr1d}| jddd\}} | jddd\}}d| d| d| }td| | |S | S )Nz--fdl.z
--fdl_tag.=.   maxsplitr   z$Fiddle setting must be of the form `z.NAME=VALUE`; `got: "z".r   r   z--zRewrote flag "%s" to "%s".)
startswithsplit
ValueErrorr   debug)r)   prefixexplicit_name_pathr$   	rewrittenr   r   r    _rewrite   s$   

z"rewrite_fdl_args.<locals>._rewrite)strlistmap)r   r8   r   r   r    rewrite_fdl_args   s   r<   c                 C   s   t t| S )zFlag parser.

  See absl.app.parse_flags_with_usage and absl.app.main(..., flags_parser).

  Args:
    args: All command line arguments.

  Returns:
    Whatever `absl.app.parse_flags_with_usage` returns. Sorry!
  )r   parse_flags_with_usager<   )r   r   r   r    flags_parser   s   r>   c                    s   t j| dd}tjD ]W}|jddd\ } fdd|D }|s= fdd|D }|r1d	| d
nd}td d| t|dkrKtd dtj	| |d dj
tj|t|d dd q
dS )a  [DEPRECATED] Sets tags based on their name, from CLI flags.

  Deprecation: This is a legacy API. Please refer to the new API usage in
  flags.py and find the documentation in flags_code_lab.md.

  Args:
    cfg: The configuration to set tags on.

  Raises:
    ValueError: If no tags with the given name are found.
    EnvironmentError: If multiple tags with the given name are found.
  T)add_superclassesr*   r,   r-   c                    s   g | ]	}|j  kr|qS r   name.0tagr@   r   r    
<listcomp>   s    zset_tags.<locals>.<listcomp>c                    s2   g | ]}   d d|j  d dv r|qS )r5    )lowerreplacerA   rB   r@   r   r    rE     s
    "z Did you mean ?rF   zNo tags with name z in config.z#There were multiple tags with name z; perhaps some module reloading weirdness is going on? This should be very rare. Please make sure that you are not dynamically creating subclasses of `fdl.Tag` and using 2 instances with the same module and name in the configuration.r   )rD   )r$   r6   )r$   N)r   	list_tags_FDL_TAGS_SETr$   r0   r1   lenEnvironmentErrorr   selectrH   r   parse_valuer9   )r"   all_tagsr&   r$   matching_tagsloose_matchesdid_you_meanr   r@   r    set_tags   s$   


rT   source_modulec                 C   sR   t jD ]#}tj|}|j}t|tjj||d}|| g|j	R i |j
 qdS )a  [DEPRECATED] Applies fiddlers to `cfg`.

  Deprecation: This is a legacy API. Please refer to the new API usage in
  flags.py and find the documentation in flags_code_lab.md.

  Args:
    cfg: The configuration to apply fiddlers to.
    source_module: source py module where fiddlers are defined.
    allow_imports: If true, then fully qualified dotted names may be used to
      specify configs or fiddlers that should be automatically imported.
  zCould not load fiddlerN)_FIDDLERr$   r   CallExpressionparse	func_nameresolve_function_referenceImportDottedNameDebugContextFIDDLERr   r   )r"   rU   allow_importsfiddler_value	call_exprfiddler_namer   r   r   r    apply_fiddlers_to  s   
ra   c                 C   s"  t jtjt }d| dd }td t  t| | durt	| dr.d| j nd}t  td| d	 t  t
| D ]A}t| |}|d
sSt|rTqBt	|drx|jrxd|j ddd }tdd|}d| }nd}td| |  qB|rt  td dS dS )z:Prints flag help, including available symbols in `module`.
   Nz#Fiddle-specific command line flags:__name__z in rF   z,Available symbols (base configs or fiddlers):r5   __doc__z

z\s+  -   zNBase configs and fiddlers may be specified using fully-qualified dotted names.)r   FLAGSmodule_helpr   modulesrd   join
splitlinesr!   hasattrdirgetattrr/   inspectismodulerf   stripr0   resub)moduler]   flags_help_textsourcerA   objobj_doc	docstringr   r   r    _print_help0  s4   
r}   c                  C   s   t tjptj} | ptjS )z5Returns True if if required Fiddle flags are defined.)bool_FDL_CONFIGr$   _FDL_CONFIG_FILE	_FDL_HELP)config_definedr   r   r    fdl_flags_suppliedR  s   
r   rw   pyref_policyc                 C   s  t j otj }tjs|rtdt jrtjrtdtjr.t| | |r+t  t	  | du rL|sLd}t jrAt
|jddtjrLt
|jddt jr~tjt j}|j}t|tjj| |d}t|rt|j|ji |j}n0||ji |j}n&tjrtj }	tj|	 |d	}W d   n1 sw   Y  ntd
t|| |d t| t | tjrt	d t	  t!"|}
|
r|
D ]}t	d|j# d|j$  qnt%d t	  t	d t	  t	t&'t()|d t  |S )a  [DEPRECATED] Returns a fdl.Buildable based on standardized flags.

  Deprecation: This is a legacy API. Please refer to the new API usage in
  flags.py and find the documentation in flags_code_lab.md.

  NOTE: the flags aren't applied in the order they are passed in on the command
  line. The order followed is:
  - all fiddlers are applied first, followed by
  - all tags, followed by
  - all overrides.

  Args:
    module: A common namespace to use as the basis for finding configs and
      fiddlers. May be `None`; if `None`, only fully qualified Fiddler imports
      will be used (or alternatively a base configuration can be specified using
      the `--fdl_config_file` flag.)
    allow_imports: If true, then fully qualified dotted names may be used to
      specify configs or fiddlers that should be automatically imported.
    pyref_policy: An optional `serialization.PyrefPolicy` to use if parsing a
      serialized Fiddle config (passed via `--fdl_config_file`).

  Returns:
    A `fdl.Buildable` based on standardized flags.
  z>At least one of --fdl_config or --fdl_config_file is required.z:--fdl_config and --fdl_config_file are mutually exclusive.NzsA module must be passed to `create_buildable_from_flags` to use the `{flag}` flag unless `allow_imports` is `True`.z--fdl_config)r&   z	--fiddlerzCould not init a buildable from)r   zThis should be unreachable.)rU   r]   z,Tags (override as --fdl_tag.<name>=<value>):ri   rh   z  No tags present in config.z2Config values (override as --fdl.<name>=<value>): )*r   r$   r   r   r   
UsageErrorr}   r   exitr!   r1   formatrV   r   rW   rX   rY   rZ   r[   BASE_CONFIGr   is_auto_configas_buildabler   r   openr   	load_jsonreadAssertionErrorra   rT   r'   r   rJ   rA   descriptionr   textwrapindentr
   as_str_flattened)rw   r]   r   missing_base_configerr_msgr_   	base_namebase_fn	buildableftagsrD   r   r   r    create_buildable_from_flagsX  sx   



r   )F)FN)2rf   rr   ru   r   r   typingr   r   r   r   abslr   r   r   etilsr	   fiddler
   r   fiddle._srcr   r   fiddle._src.absl_flagsr   fiddle._src.experimentalr   r   DEFINE_stringr   DEFINE_pathr   DEFINE_multi_stringrV   r#   rK   DEFINE_boolr   r!   	Buildabler'   r9   r<   r>   rT   ra   r}   r~   r   PyrefPolicyr   r   r   r   r    <module>   s   h%'

"