Skip to content

Commit

Permalink
updating documentation (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncilfone committed Jan 26, 2022
1 parent 8ed2f75 commit a8e7547
Show file tree
Hide file tree
Showing 16 changed files with 287 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
debug/

# Auto-gen Reference Docs
website/docs/reference
website/docs/reference/**/*.md

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,23 @@

`spock` is a framework that helps users easily define, manage, and use complex parameter configurations within Python
applications. It lets you focus on the code you need to write instead of re-implementing boilerplate code such as
creating ArgParsers, reading configuration files, handling dependencies, type validation, implementing traceability etc.
creating ArgParsers, reading configuration files, handling dependencies, implementing type validation,
maintaining traceability, etc.

`spock` configurations are normal python classes that are decorated with `@spock`. It supports
inheritance, dynamic class dependencies, loading/saving configurations from/to multiple markdown formats, automatically
generating CLI arguments, and hierarchical configuration by composition.

## 💥 Why You Should Use Spock 💥

* Simple and organized parameter defintions (i.e. a single line)
* Statically type checked & frozen parameters (i.e. fail early during long ML training runs)
* Simple organized parameter definitions (i.e. a single line)
* Type checked (static-eqsue) & frozen parameters (i.e. fail early during long ML training runs)
* Complex parameter dependencies made simple (i.e. `@spock` class with a parameter that is an Enum of other
`@spock` classes)
* Fully serializable parameter state (i.e. reproduce prior runtime parameter configurations)
* Automatic CLI generation without argparser boilerplate (i.e click or typer for free!)
* Unified hyper-parameter definitions (i.e. don't write different definitions for Ax or Optuna)
* Fully serializable parameter state(s) (i.e. exactly reproduce prior runtime parameter configurations)
* Automatic type checked CLI generation w/o argparser boilerplate (i.e click and/or typer for free!)
* Easily maintain parity between CLIs and Python APIs (i.e. single line changes between CLI and Python API definitions)
* Unified hyper-parameter definitions and interface (i.e. don't write different definitions for Ax or Optuna)

## Key Features

Expand Down Expand Up @@ -95,11 +97,17 @@ See [Releases](https://github.com/fidelity/spock/releases) for more information.

<details>

#### January 26th, 2022
* Added `evolve` support to the underlying `SpockBuilder` class. This provides functionality similar to the underlying
attrs library ([attrs.evolve](https://www.attrs.org/en/stable/api.html#attrs.evolve)). `evolve()` creates a new
`Spockspace` instance based on differences between the underlying declared state and any passed in instantiated
`@spock` decorated classes.

#### January 18th, 2022
* Support for lazy evaluation: (1) inherited classes do not need to be `@spock` decorated, (2) dependencies/references
between `spock` classes can be lazily handled thus preventing the need for every `@spock` decorated classes to be
passed into `*args` within the main `SpockBuilder` API
* Updated main API interface for better top-level imports (backwards compatible): `ConfigArgBuilder`->`spockBuilder`
* Updated main API interface for better top-level imports (backwards compatible): `ConfigArgBuilder`->`SpockBuilder`
* Added stubs to the underlying decorator that should help with type hinting in VSCode (pylance/pyright)

#### December 14, 2021
Expand Down
6 changes: 5 additions & 1 deletion spock/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,11 +581,15 @@ def evolve(self, *args: typing.Type[_CLS]):
allows for class evolution -- returns a new Spockspace object
Args:
*args: variable number of @spock decorated classes to evolve parameters with
*args: variable number of instantiated @spock decorated classes to evolve parameters with
Returns:
new_arg_namespace: Spockspace evolved with *arg @spock decorated classes
Raises:
_SpockEvolveError: if multiple of the same instance are passed as input or if the one or more of the inputs
are not within the set of original input classes
"""
# First check that all instances are in the underlying set of input_classes and that there are no dupes
arg_counts = Counter([type(v).__name__ for v in args])
Expand Down
4 changes: 2 additions & 2 deletions website/docs/ArgParser-Replacement.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ valid command-line arguments:
Simply do not pass a `-c` or `--config` argument at the command line and instead pass in all values to the
automatically generated cmd-line arguments.

```bash
$ python simple.py --ExampleConfig.read_path /my/file/path --ExampleConfig.date 1292838124 \
```shell
python simple.py --ExampleConfig.read_path /my/file/path --ExampleConfig.date 1292838124 \
--ExampleConfig.cache_path /path/to/cache/dir
```
10 changes: 5 additions & 5 deletions website/docs/Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
### Install/Upgrade

#### PyPi
```bash
```shell
pip install spock-config
```

#### w/ S3 Extension

Extra Dependencies: boto3, botocore, hurry.filesize, s3transfer

```bash
```shell
pip install spock-config[s3]
```

Expand All @@ -27,17 +27,17 @@ Requires Python 3.7+

Extra Dependencies: optuna, ax-platform, torch, torchvision, mypy_extensions (Python < 3.8)

```bash
```shell
pip install spock-config[tune]
```

#### Pip From Source
```bash
```shell
pip install git+https://github.com/fidelity/spock
```

#### Build From Source
```bash
```shell
git clone https://github.com/fidelity/spock
cd spock
pip install setuptools wheel
Expand Down
10 changes: 5 additions & 5 deletions website/docs/Quick-Start.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,17 @@ most_fancy_parameter: [768, 768, 512, 128]

Finally, we would run our script and pass the path to the configuration file to the command line (`-c` or `--config`):

```bash
$ python simple.py -c simple.yaml
```shell
python simple.py -c simple.yaml
```

To get help for our `spock` class and defined parameters:

```bash
$ python simple.py --help
```shell
python simple.py --help
```

```bash
```shell
usage: /Users/a635179/Documents/git_repos/open_source/spock/examples/quick-start/simple.py -c [--config] config1 [config2, config3, ...]

Quick start example
Expand Down
6 changes: 3 additions & 3 deletions website/docs/addons/S3.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ it to the `SpockBuilder`.

Install `spock` with the extra s3 related dependencies.

```bash
```shell
pip install spock-config[s3]
```

Expand Down Expand Up @@ -90,8 +90,8 @@ def main():
Usually we pass a relative or absolute system path as the configuration file command line argument. Here we pass
in a S3 URI instead:

```bash
$ python simple.py -c s3://my-bucket/path/to/file/config.yaml
```shell
python simple.py -c s3://my-bucket/path/to/file/config.yaml
```

With a `S3Config` object passed into the `SpockBuilder` the S3 URI will automatically be handled by `spock`.
Expand Down
2 changes: 1 addition & 1 deletion website/docs/addons/tuner/About.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ All examples can be found [here](https://github.com/fidelity/spock/blob/master/e

Install `spock` with the extra hyper-parameter tuning related dependencies. Requires Python 3.7+ due to ax-platform

```bash
```shell
pip install spock-config[tune]
```

Expand Down
16 changes: 8 additions & 8 deletions website/docs/advanced_features/Command-Line-Overrides.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,23 @@ class SGDConfig(OptimizerConfig):

To run `tutorial.py` we would normally pass just the path to the configuration file as a command line argument:

```bash
$ python tutorial.py --config tutorial.yaml
```shell
python tutorial.py --config tutorial.yaml
```

But with command line overrides we can also pass parameter arguments to override their value within the configuration
file:

```bash
$ python tutorial.py --config tutorial.yaml --DataConfig.cache_path /tmp/trash
```shell
python tutorial.py --config tutorial.yaml --DataConfig.cache_path /tmp/trash
```

Each parameter can be overridden **ONLY** at the class specific level with the syntax `--classname.parameter`. For
instance, our previous example would only override the `DataConfig.cache_path` and not the `ModelConfig.cache_path` even
though they have the same parameter name (due to the different class names).

```bash
$ python tutorial.py --config tutorial.yaml --DataConfig.cache_path /tmp/trash
```shell
python tutorial.py --config tutorial.yaml --DataConfig.cache_path /tmp/trash
```

### Overriding Nested `@spock` Classes
Expand Down Expand Up @@ -204,8 +204,8 @@ NestedListStuff:

We could override the parameters like so (note that the len must match the defined length from the YAML):

```bash
$ python tutorial.py --config tutorial.yaml --TypeConfig.nested_list.NestedListStuff.one [1,2] \
```shell
python tutorial.py --config tutorial.yaml --TypeConfig.nested_list.NestedListStuff.one [1,2] \
--TypeConfig.nested_list.NestedListStuff.two ['ciao','ciao']
```

Expand Down
151 changes: 151 additions & 0 deletions website/docs/advanced_features/Evolve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Evolve

`spock` provides evolve functionality similar to the underlying attrs library
([attrs.evolve](https://www.attrs.org/en/stable/api.html#attrs.evolve). `evolve()` creates a new
`Spockspace` instance based on differences between the underlying declared state and any passed in instantiated
`@spock` decorated classes.

### Using Evolve

The `evolve()` method is available form the `SpockBuilder` object. `evolve()` takes as input a variable number of
instantiated `@spock` decorated classes, evolves the underlying `attrs` objects to incorporate the changes between
the instantiated classes and the underlying classes, and returns the new `Spocksspace` object.

For instance:

```python

from enum import Enum
from spock import spock
from spock import SpockBuilder


class Choices(Enum):
choice1 = 1
choice2 = 2


@spock
class Configs4OneThing:
the_choice: Choices = Choices.choice1
param: int = 10


def main():
evolve_class = Configs4OneThing(param=20)
evolved_configs = SpockBuilder(Configs4OneThing, desc='Evolve Example').evolve(evolve_class)
print(evolved_configs)

if __name__ == '__main__':
main()
```

This would evolve the value of `param` to be 10 instead of the defulat value of 10. The print output woulf be:

```shell
Configs4OneThing: !!python/object:spock.backend.config.Configs4OneThing
param: 20
the_choice: 1
```

### Maintaining CLI and Python API Configuration Parity

`evolve` is quite useful when writing python code/libraries/packages that maintain both a CLI and a Python API. With
`spock` it is simple to maintain parity between the CLI and the Python API by leveraging the `evolve` functionality.

For instance, let's say we have two different `@spock` decorated configs we want to use for both the CLI and the Python
API:

```python
# config.py

from enum import Enum
from spock import spock
from typing import List


class Choices(Enum):
choice1 = 1
choice2 = 2


@spock
class Configs4OneThing:
the_choice: Choices = Choices.choice1
param: int = 10


@spock
class Configs4AnotherThing:
some_list: List[float] = [10.0, 20.0]
flag: bool = False


# List of all configs
ALL_CONFIGS = [
Configs4OneThing,
Configs4AnotherThing
]

```

With these `@spock` decorated classes it's easy to write a parent class that contains shared functionality (i.e. run a
model, do some work, etc.) and two child classes that handle the slightly different syntax needed for the underlying
`SpockBuilder` for the CLI and for the Python API.

For the CLI, we use the common `spock` syntax that has been shown in previous examples/tutorial. Call the builder
object and pass in all `@spock` decorated classes. Keep the `no_cmd_line` flag set to `False` which will automatically
generate a command line argument for each defined parameter and provide support for the `--config` argument to pass
in values via a markdown file(s). We then call `generate` on the builder to return the `Spockspace`.

For the Python API, we modify the `spock` syntax slightly. We still pass in all `@spock` decorated classes but set
the `no_cmd_line` flag to `True` to prevent command line arguments (and markdown configuration). We then call `evolve`
and pass in any user instantiated `@spock` decorated classes to evolve the underlying object and return a new
`Spockspace` object that has been evolved based on the differences between the values within instantiated classes and
the values in the underlying object.

Example code is given below:

```python
# code.py
from abc import ABC
from spock import SpockBuilder
from config import ALL_CONFIGS


class Base(ABC):
def run(self):
# do something with self.configs
...


class OurAPI(Base):
def __init__(self,
config_4_one_thing: Configs4OneThing = Configs4OneThing(),
config_4_another_thing: Configs4AnotherThing = Configs4AnotherThing()
):
# Call the SpockBuilder with the no_cmd_line flag set to True
# This will prevent command-line arguments from being generated
# Additionally call evolve on the builder with the custom/default Configs4OneThing & Configs4AnotherThing
# objects
self.configs = SpockBuilder(*ALL_CONFIGS, no_cmd_line=True, configs=[]).evolve(
config_4_one_thing, config_4_another_thing
)


class OurCLI(Base):
def __init__(self):
# Call the SpockBuilder with the no_cmd_line flag set to False (default value)
# This will automatically provide command-line arguments for all of the @spock decorated
# config classes
self.configs = SpockBuilder(*ALL_CONFIGS).generate()


def cli_shim():
"""Shim function for setup.py entry_points
Returns:
None
"""
cli_runner = OurCLI().run()
```
4 changes: 2 additions & 2 deletions website/docs/advanced_features/Keyword-Configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def main():

Now to run `tutorial.py` we don't need to pass a command line argument:

```bash
$ python tutorial.py
```shell
python tutorial.py
```

### Specifying The Config Keyword Argument & The No Command Line Flag
Expand Down
6 changes: 3 additions & 3 deletions website/docs/basics/Define.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ class ModelConfig:

If we run our `tutorial.py` script with the `--help` flag:

```bash
$ python tutorial.py --help
```shell
python tutorial.py --help
```

We should see the help information we added to the docstring(s):

```bash
```shell
usage: /Users/a635179/Documents/git_repos/open_source/spock/examples/tutorial/basic/tutorial.py -c [--config] config1 [config2, config3, ...]

spock Basic Tutorial
Expand Down
Loading

0 comments on commit a8e7547

Please sign in to comment.