Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Проектируем новый статический анализатор​

Проектируем новый статический анализатор​

Никита Соболев (СTO Wemake.services и Drylabs.io) @ Moscow Python №75
"В питоне на рынке статических анализаторов — все очень плохо. Мы должны сделать все с нуля. Что нам потребуется? От чего должны отказаться?"

Видео: https://moscowpython.ru/meetup/75/new-static-analyzer/
Менторинг команд от Drylabs: https://drylabs.io/team-mentoring

Moscow Python Meetup

May 27, 2021
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Flake8 Плагины pylint Плагины mypy Плагины pylance pytype pyre-check black

    autopep8 fixit yapf pyupgrade Тайпчекеры Линтеры 19
  2. Flake8 Плагины pylint Плагины mypy Плагины pylance pytype pyre-check black

    autopep8 fixit yapf pyupgrade Тайпчекеры Линтеры Авто-форматеры 20
  3. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 24
  4. Проблемы > Неконсистентность между версиями Python > Нет информации о

    типах > Нет нюансов синтаксиса > Нет комментариев 26
  5. 29

  6. Проблемы > Представляет собой не "структуру", а простой список >

    Вообще ничего не знает / не умеет > Очень сложен в работе для правил с хоть какой-то логикой 31
  7. Используется > В инфраструктуре Инстаграма > Во многих новых библиотеках

    для авто-форматирования > Для проверки нюансов синтаксиса 33
  8. Используется > В инфраструктуре Инстаграма > Во многих новых библиотеках

    для авто-форматирования > Для проверки нюансов синтаксиса > Для анализа кода 33
  9. Проблемы > Сбоит на некоторых синтаксических конструкциях Python3.9 https:// github.com/Instagram/LibCST/

    issues/490 > Не работает с Python3.10+ https:// github.com/Instagram/LibCST/ issues/285 34
  10. Используется > В любом тайпчекере для представления нашего кода с

    типами > Для каждого тайпчекера - оно свое 36
  11. Проблемы > У каждой реализации они свои! > Нет единого

    стандарта > В каждом тайпчекере поддерживается далеко не все возможности 37
  12. Проблемы > У каждой реализации они свои! > Нет единого

    стандарта > В каждом тайпчекере поддерживается далеко не все возможности > Нигде (кроме mypy) нет возможности подключать плагины 37
  13. Pylint > Примитивная конфигурация > Нет типов > Но лучше

    бы их совсем не было > Невозможно нормально внедрить 40
  14. 44

  15. typed-linter > Нормальная конфигурация в духе eslint > Провайдеры типов

    > Гибкая система плагинов > Авто-форматирование 49
  16. typed-linter > Нормальная конфигурация в духе eslint > Провайдеры типов

    > Гибкая система плагинов > Авто-форматирование > Нормальное внедрение 49
  17. [flake8] format = wemake show-source = True statistics = False

    doctests = True # darglint configuration: # https://github.com/terrencepreilly/darglint strictness = long docstring-style = numpy # Plugins: max-complexity = 6 max-line-length = 80 # wemake-python-styleguide settings: i-control-code = False 52
  18. 54

  19. extends: - typed-linter/recommended - 'url:https://path-to-my-config/file.yml' - 'file:local/path/file.yml' checks: - name:

    typed-linter/new-style-generics level: error extras: allow-old-style: [typing.List] settings: type-engine: name: typed-linter/mypy reporter: name: typed-linter/rich 56
  20. extends: - typed-linter/recommended - 'url:https://path-to-my-config/file.yml' - 'file:local/path/file.yml' checks: - name:

    typed-linter/new-style-generics level: error extras: allow-old-style: [typing.List] settings: type-engine: name: typed-linter/mypy reporter: name: typed-linter/rich 57
  21. extends: - typed-linter/recommended - 'url:https://path-to-my-config/file.yml' - 'file:local/path/file.yml' checks: - name:

    typed-linter/new-style-generics level: error extras: allow-old-style: [typing.List] settings: type-engine: name: typed-linter/mypy reporter: name: typed-linter/rich 58
  22. checks: - name: typed-linter/eqeq-is-mixup level: none overrides: - glob: some/**/*.py

    checks: - name: typed-linter/new-style-generics level: none type-overrides: - astpath: | \\ClassDef[type=django.url.URLConf]\method[name=locate] checks: - name: typed-linter/hidden-any level: none 60
  23. checks: - name: typed-linter/eqeq-is-mixup level: none overrides: - glob: some/**/*.py

    checks: - name: typed-linter/new-style-generics level: none type-overrides: - astpath: | \\ClassDef[type=django.url.URLConf]\method[name=locate] checks: - name: typed-linter/hidden-any level: none 61
  24. unload_task = _unload_to_file(export_config) if not export_config.is_incremental: _latest_only(export_config.athena_table_name) >> unload_task unload_task

    \ >> _upload_to_s3(export_config.athena_table_name) \ >> [ _clear_export_dir_and_logs(export_config.athena_table_name), *_repair_tables(export_config), ] 64
  25. checks: - name: typed-linter/eqeq-is-mixup level: none overrides: - glob: some/**/*.py

    checks: - name: typed-linter/new-style-generics level: none type-overrides: - astpath: | \\ClassDef[type=django.url.URLConf]\method[name=locate] checks: - name: typed-linter/hidden-any level: none 65
  26. class BasePlugin(object): def __init__(self, name: str, checks: CheckTypeSpec): ... @classmethod

    def load_checks( cls, configuration: Configuration, loader: ConventionalLoader, ) -> Sequence[CheckTypeSpec]: ... def run_project_checks( self, type_engine: BaseTypeEngine, project_modules: ProjectModules, configuration: Configuration, ) -> Iterable[BaseViolation]: ... def run_module_checks( self, type_checker: BaseTypeChecker, configuration: Configuration, module_spec: ModuleSpec, ) -> Iterable[BaseViolation]: ...
  27. 70

  28. 71

  29. extends: - typed-linter/recommended - flake8/recommended overrides: - glob: some/**/*.py checks:

    - name: flake8/F001 level: none settings: type-engine: name: your-project/pytype reporter: name: circle-ci/json 72
  30. 74

  31. extends: - typed-linter/recommended - flake8/recommended overrides: - glob: some/**/*.py checks:

    - name: flake8/F001 level: none settings: type-engine: name: your-project/pytype reporter: name: circle-ci/json 75
  32. extends: - typed-linter/recommended - flake8/recommended overrides: - glob: some/**/*.py checks:

    - name: flake8/F001 level: none settings: type-engine: name: your-project/pytype reporter: name: circle-ci/json 77
  33. Как оно будет? > typed-linter --baseline myproject > Все ошибки

    запомнились > typed-linter --baseline myproject 80
  34. Как оно будет? > typed-linter --baseline myproject > Все ошибки

    запомнились > typed-linter --baseline myproject > Старых ошибок нет 80
  35. Как оно будет? > typed-linter --baseline myproject > Все ошибки

    запомнились > typed-linter --baseline myproject > Старых ошибок нет > Новые делать нельзяы 80
  36. Проблемы > В текущей реализации очень сложно идентифицировать ошибку и

    привязать её к определенному месту > Раньше мы оперировали только: строками, позицией, кодом ошибки 81
  37. 85