Skip to content

Commit

Permalink
documentation improvements (#410)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Jan 12, 2022
1 parent c78bfb2 commit efa1bee
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 59 deletions.
43 changes: 6 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,13 @@ But note that this will try to import all Python files it is passed. If you have

In order to run successfully, pyanalyze needs to be able to import the code it checks. To make this work you may have to manually adjust Python's import path using the `$PYTHONPATH` environment variable.

Pyanalyze has a number of command-line options, which you can see by running `python -m pyanalyze --help`. Important ones include `-f`, which runs an interactive prompt that lets you examine and fix each error found by pyanalyze, and `--enable`/`--disable`, which enable and disable specific error codes.

### Advanced usage

At Quora, when we want pyanalyze to check a library in CI, we write a unit test that invokes pyanalyze for us. This allows us to run pyanalyze with other tests without further special setup, and it provides a convenient place to put configuration options. An example is pyanalyze's own `test_self.py` test:

```python
import os.path
import pyanalyze
from pyanalyze.error_code import ErrorCode
from pyanalyze.test_node_visitor import skip_before


class PyanalyzeConfig(pyanalyze.config.Config):
DEFAULT_DIRS = (str(os.path.dirname(__file__)),)
DEFAULT_BASE_MODULE = pyanalyze
ENABLED_ERRORS = {
ErrorCode.possibly_undefined_name,
ErrorCode.use_fstrings,
ErrorCode.missing_return_annotation,
ErrorCode.missing_parameter_annotation,
ErrorCode.unused_variable,
}


class PyanalyzeVisitor(pyanalyze.name_check_visitor.NameCheckVisitor):
config = PyanalyzeConfig()
should_check_environ_for_files = False


@skip_before((3, 6))
def test_all():
PyanalyzeVisitor.check_all_files()
### Configuration

Pyanalyze has a number of command-line options, which you can see by running `python -m pyanalyze --help`. Important ones include `-f`, which runs an interactive prompt that lets you examine and fix each error found by pyanalyze, and `--enable`/`--disable`, which enable and disable specific error codes.

if __name__ == "__main__":
PyanalyzeVisitor.main()
```
Configuration through a `pyproject.toml` file is also supported. See
[the documentation](https://pyanalyze.readthedocs.io/en/latest/configuration.html) for
details.

### Extending pyanalyze

Expand Down Expand Up @@ -177,7 +146,7 @@ You can add an error code, like `# static analysis: ignore[undefined_name]`, to

### Python version support

Pyanalyze supports Python 3.6 through 3.9. Because it imports the code it checks, you have to run it using the same version of Python you use to run your code.
Pyanalyze supports Python 3.6 through 3.10. Because it imports the code it checks, you have to run it using the same version of Python you use to run your code.

## Developing pyanalyze

Expand Down
2 changes: 1 addition & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
- Add `pyanalyze.extensions.CustomCheck`
- Add `pyanalyze.extensions.ExternalType`
- If you have code dealing with `Value` objects, note that there are several changes:
- The `UnresolvedValue` class was renamed to `AnyValue`.
- The `UnresolvedValue` class was renamed to `AnyValue`.
- `value is UNRESOLVED_VALUE` will no longer be reliable. Use `isinstance(value, AnyValue)` instead.
- `TypedDictValue` now stores whether each key is required or not in its `items` dictionary.
- `UnboundMethodValue` now stores a `Composite` object instead of a `Value` object, and has a new
Expand Down
9 changes: 5 additions & 4 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ duplicate_dict_key = true

It is recommended to always set the following configuration options:

* *paths*: A list of paths (relative to the location of the `pyproject.toml` file) that pyanalyze should check by default.
* *import_paths*: A list of paths (also relative to the configuration file) that pyanalyze should use as roots when trying to import files it is checking. If this is not set, pyanalyze will use entries from `sys.path`, which may produce unexpected results.
- _paths_: A list of paths (relative to the location of the `pyproject.toml` file) that pyanalyze should check by default.
- _import_paths_: A list of paths (also relative to the configuration file) that pyanalyze should use as roots when trying to import files it is checking. If this is not set, pyanalyze will use entries from `sys.path`, which may produce unexpected results.

Other supported configuration options are listed below.

Expand All @@ -47,6 +47,7 @@ extend_config = "../path/to/other/pyproject.toml"

Options set in the included config file have lower priority.

Most configuration options can also be set on the command line.
Most configuration options can also be set on the command line. Run
`pyanalyze --help` to see these options.

<!-- TODO figure out a way to dynamically include docs for each option -->
<!-- TODO figure out a way to dynamically include docs for each option -->
18 changes: 9 additions & 9 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@ However, this approach also has some disadvantages:

- It is difficult to engineer an incremental mode, because that would require reloading
imported modules. At Quora, we run pyanalyze locally on changed files only, which is
much faster than running it on the entire codebase but does not catch issues where a
much faster than running it on the entire codebase but does not catch issues where a
change breaks type checking in an upstream module.
- Scripts that do work on import cannot be usefully checked; you must guard execution
with `if __name__ == "__main__":`.
- Undefined attributes on instances of user-defined classes cannot be detected with full
confidence, because the class object does not provide a good way to find out which
confidence, because the class object does not provide a good way to find out which
attributes are created in the `__init__` method. Currently, pyanalyze works around this
by deferring detection of undefined attributes until the entire codebase has been checked,
but this is fragile and not always reliable.
- The implementation of `@typing.overload` does not provide a way to access the overloads
at runtime, so there is no obvious way to support overloaded functions at runtime.
(Note that although there is no technical blocker for supporting overloads in stub files,
(Note that although there is no technical blocker for supporting overloads in stub files,
pyanalyze does not support overloads in stubs yet either.)

## When should I use pyanalyze?

If you have a complex Python codebase and want to make sure it stays maintainable and
If you have a complex Python codebase and want to make sure it stays maintainable and
stable, using a type checker is a great option. There are several options for type
checking Python code (listed in
checking Python code (listed in
[the typing documentation](https://typing.readthedocs.io/en/latest/)). Unique advantages
of pyanalyze include:

Expand All @@ -76,12 +76,12 @@ of pyanalyze include:
finding missing `await` statements, detecting possibly undefined names, and warning
about conditions on objects of suspicious types.
- Type system extensions such as `CustomCheck`, `ParameterTypeGuard`, and `ExternalType`.
- Strong support for checking code that uses the [asynq](https://github.com/quora/asynq)
- Strong support for checking code that uses the [asynq](https://github.com/quora/asynq)
framework.

## What is the history of pyanalyze?

[//]: # (First commit is 6d671398f9de24ee8cc1baccadfed2420cba765c)
[//]: # "First commit is 6d671398f9de24ee8cc1baccadfed2420cba765c"

The first incarnation of pyanalyze dates back to July 2015 and merely detected undefined
names. It was soon extended to detect incompatible function calls, find unused
Expand All @@ -92,10 +92,10 @@ For context, pytype was started in March 2015 and mypy in 2012. PEP 484 was acce
The initial version was closely tied to
Quora's internal code structure, but in June 2017 it was split off into its own internal
package, now named pyanalyze. By then, it had strong support for `asynq` and supported
customization through implementation functions. After Quora moved to Python 3, pyanalyze
customization through implementation functions. After Quora moved to Python 3, pyanalyze
gained support for parsing type annotations and
its typing support moved closer to the standard type system.

The first public release was in May 2020. Since then, work has focused on providing full
support for the Python type system, including `Annotated`, `Callable`, `TypeVar`, and
`TypeGuard`.
`TypeGuard`.
12 changes: 6 additions & 6 deletions docs/type_evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -691,11 +691,10 @@ The last option could look like this:
def zip(strict: bool = False):
if is_provided(strict) and sys.version_info < (3, 10):
show_error(
added_in_py_version("strict", "3.10"),
added_in_py_version("strict", "3.10"),
argument="strict"
)


### Adding attributes

A common pattern in type checker plugins is for the plugin
Expand Down Expand Up @@ -738,14 +737,15 @@ in pyanalyze:

Currently unsupported features include:

- pyanalyze should provide a way to register
an evaluation function for a runtime function,
to replace some impls.
- Type compatibility for evaluated functions.
- Overloaded evaluated functions.

Areas that need more thought include:

- Interaction with typevars
- Interaction with overloads. It should be possible
to register multiple evaluation functions for a
function, treating them as overloads.
- Interaction with `__init__` and self types. How does
an eval function set the self type of a function? Perhaps
we can have the return type have special meaning just for
`__init__` methods.
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@
"ast_decompiler>=0.4.0",
"typeshed_client>=2.0.0",
"typing_inspect>=0.7.0",
"typing_extensions>=3.10.0.0",
"mypy_extensions",
"typing_extensions>=4.0.0",
"aenum>=2.2.3",
"codemod",
"tomli>=1.1.0",
Expand Down

0 comments on commit efa1bee

Please sign in to comment.