diff --git a/.pylintrc b/.pylintrc index 1d46aeef9..60a4f9116 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,10 +1,5 @@ [MASTER] # inclusive-language: ignore -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-allowlist=mypy - # Add files or directories to the blocklist. They should be base names, not # paths. ignore=CVS @@ -28,7 +23,7 @@ limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint.extensions.no_self_use # Pickle collected data for later comparisons. persistent=yes @@ -60,11 +55,23 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=bad-continuation, # Rely on yapf for formatting +disable=broad-exception-raised, + consider-iterating-dictionary, + consider-using-f-string, + consider-using-generator, + consider-using-in, consider-using-with, fixme, - subprocess-run-check, + implicit-str-concat, raise-missing-from, + redundant-u-string-prefix, + subprocess-run-check, + superfluous-parens, + unnecessary-lambda-assignment, + unspecified-encoding, + use-dict-literal, + use-list-literal, + # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -131,13 +138,6 @@ max-line-length=80 # Maximum number of lines in a module. max-module-lines=9999 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma, - dict-separator - # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no @@ -161,7 +161,7 @@ allow-global-unused-variables=yes callbacks=cb_, _cb -# A regular expression matching the name of placeholder variables (i.e. +# A regular expression matching the name of placeholder variables (i.e. # expected to not be used). # inclusive-language: ignore dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ @@ -309,7 +309,7 @@ good-names=i, _ # Include a hint for the correct naming format with invalid-name. -include-naming-hint=no +include-naming-hint=yes # Naming style matching correct inline iteration names. inlinevar-naming-style=any @@ -511,5 +511,5 @@ preferred-modules= # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception diff --git a/pw_build/py/pw_build/project_builder_context.py b/pw_build/py/pw_build/project_builder_context.py index 39347ad80..32508a9e3 100644 --- a/pw_build/py/pw_build/project_builder_context.py +++ b/pw_build/py/pw_build/project_builder_context.py @@ -205,7 +205,7 @@ def startup_progress(self) -> None: bottom_toolbar=self.bottom_toolbar, cancel_callback=self.ctrl_c_interrupt, ) - self.progress_bar.__enter__() + self.progress_bar.__enter__() # pylint: disable=unnecessary-dunder-call self.create_title_bar_container() self.progress_bar.app.layout.container.children[ # type: ignore @@ -216,7 +216,7 @@ def startup_progress(self) -> None: def exit_progress(self) -> None: if not self.progress_bar: return - self.progress_bar.__exit__() + self.progress_bar.__exit__() # pylint: disable=unnecessary-dunder-call def clear_progress_scrollback(self) -> None: if not self.progress_bar: diff --git a/pw_cli/py/setup.cfg b/pw_cli/py/setup.cfg index be8343850..17eeaf9f2 100644 --- a/pw_cli/py/setup.cfg +++ b/pw_cli/py/setup.cfg @@ -21,6 +21,9 @@ description = Pigweed swiss-army knife [options] packages = find: zip_safe = False +install_requires = + pyyaml + toml [options.entry_points] console_scripts = pw = pw_cli.__main__:main diff --git a/pw_console/py/pw_console/log_filter.py b/pw_console/py/pw_console/log_filter.py index a2c4def57..60411efc1 100644 --- a/pw_console/py/pw_console/log_filter.py +++ b/pw_console/py/pw_console/log_filter.py @@ -92,7 +92,7 @@ class LogFilter: field: Optional[str] = None def pattern(self): - return self.regex.pattern + return self.regex.pattern # pylint: disable=no-member def matches(self, log: LogLine): field = log.ansi_stripped_log @@ -110,7 +110,7 @@ def matches(self, log: LogLine): elif self.field == 'time': field = log.record.asctime - match = self.regex.search(field) + match = self.regex.search(field) # pylint: disable=no-member if self.invert: return not match @@ -143,7 +143,9 @@ def apply_highlighting(fragments, i): apply_highlighting(exploded_fragments, i) else: # Highlight each non-overlapping search match. - for match in self.regex.finditer(line_text): + for match in self.regex.finditer( # pylint: disable=no-member + line_text + ): # pylint: disable=no-member for fragment_i in range(match.start(), match.end()): apply_highlighting(exploded_fragments, fragment_i) diff --git a/pw_console/py/pw_console/log_line.py b/pw_console/py/pw_console/log_line.py index 6543e30ae..0277166af 100644 --- a/pw_console/py/pw_console/log_line.py +++ b/pw_console/py/pw_console/log_line.py @@ -43,7 +43,9 @@ def update_metadata(self, extra_fields: Optional[Dict] = None): """Parse log metadata fields from various sources.""" # 1. Parse any metadata from the message itself. - self.metadata = FormatStringWithMetadata(str(self.record.message)) + self.metadata = FormatStringWithMetadata( + str(self.record.message) # pylint: disable=no-member + ) # pylint: disable=no-member self.formatted_log = self.formatted_log.replace( self.metadata.raw_string, self.metadata.message ) @@ -65,9 +67,9 @@ def update_metadata(self, extra_fields: Optional[Dict] = None): # See: # https://docs.python.org/3/library/logging.html#logging.debug if hasattr(self.record, 'extra_metadata_fields') and ( - self.record.extra_metadata_fields # type: ignore + self.record.extra_metadata_fields # type: ignore # pylint: disable=no-member ): - fields = self.record.extra_metadata_fields # type: ignore + fields = self.record.extra_metadata_fields # type: ignore # pylint: disable=no-member for key, value in fields.items(): self.metadata.fields[key] = value diff --git a/pw_console/py/pw_console/progress_bar/progress_bar_state.py b/pw_console/py/pw_console/progress_bar/progress_bar_state.py index 37bcc068e..159cc3165 100644 --- a/pw_console/py/pw_console/progress_bar/progress_bar_state.py +++ b/pw_console/py/pw_console/progress_bar/progress_bar_state.py @@ -76,7 +76,7 @@ def handle_sigint(_signum, _frame): # Shut down the ProgressBar prompt_toolkit application prog_bar = self.instance if prog_bar is not None and hasattr(prog_bar, '__exit__'): - prog_bar.__exit__() + prog_bar.__exit__() # pylint: disable=unnecessary-dunder-call raise KeyboardInterrupt signal.signal(signal.SIGINT, handle_sigint) @@ -95,7 +95,7 @@ def startup_progress_bar_impl(self): ) # Start the ProgressBar prompt_toolkit application in a separate # thread. - prog_bar.__enter__() + prog_bar.__enter__() # pylint: disable=unnecessary-dunder-call self.instance = prog_bar return self.instance diff --git a/pw_console/py/pw_console/pw_ptpython_repl.py b/pw_console/py/pw_console/pw_ptpython_repl.py index c9b2f4a54..079ba096f 100644 --- a/pw_console/py/pw_console/pw_ptpython_repl.py +++ b/pw_console/py/pw_console/pw_ptpython_repl.py @@ -279,7 +279,7 @@ def user_code_complete_callback(self, input_text, future): # Trigger a prompt_toolkit application redraw. self.repl_pane.application.application.invalidate() - async def _run_system_command( + async def _run_system_command( # pylint: disable=no-self-use self, text, stdout_proxy, _stdin_proxy ) -> int: """Run a shell command and print results to the repl.""" diff --git a/pw_env_setup/py/pw_env_setup/environment.py b/pw_env_setup/py/pw_env_setup/environment.py index ca7f7f46c..c87de8847 100644 --- a/pw_env_setup/py/pw_env_setup/environment.py +++ b/pw_env_setup/py/pw_env_setup/environment.py @@ -483,6 +483,7 @@ def __call__(self, export=True): Yields the new environment object. """ + orig_env = {} try: if export: orig_env = os.environ.copy() diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list index 10970a015..fa23b47b9 100644 --- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list +++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list @@ -1,6 +1,6 @@ alabaster==0.7.12 appdirs==1.4.4 -astroid==2.6.6 +astroid==2.14.2 Babel==2.9.1 backcall==0.2.0 build==0.8.0 @@ -12,6 +12,7 @@ coloredlogs==15.0.1 coverage==6.3 cryptography==36.0.1 decorator==5.1.1 +dill==0.3.6 docutils==0.17.1 breathe==4.34.0 google-api-core==2.7.1 @@ -41,6 +42,7 @@ parameterized==0.8.1 parso==0.8.3 pep517==0.12.0 pexpect==4.8.0 +platformdirs==3.0.0 pickleshare==0.7.5 prompt-toolkit==3.0.36 protobuf==3.20.1 @@ -51,7 +53,7 @@ pyasn1-modules==0.2.8 pycparser==2.21 pyelftools==0.27 Pygments==2.14.0 -pylint==2.9.3 +pylint==2.16.2 pyparsing==3.0.6 pyperclip==1.8.2 pyserial==3.5 @@ -76,6 +78,7 @@ sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 toml==0.10.2 tomli==2.0.0 +tomlkit==0.11.6 traitlets==5.1.1 types-docutils==0.17.4 types-futures==3.3.2 diff --git a/pw_ide/py/pw_ide/editors.py b/pw_ide/py/pw_ide/editors.py index 311f9a9ed..f59e35346 100644 --- a/pw_ide/py/pw_ide/editors.py +++ b/pw_ide/py/pw_ide/editors.py @@ -123,14 +123,14 @@ def dump(self, data: OrderedDict, *args, **kwargs) -> None: # Allows constraining to dicts and dict subclasses, while also constraining to # the *same* dict subclass. -_TDictLike = TypeVar('_TDictLike', bound=Dict) +_DictLike = TypeVar('_DictLike', bound=Dict) def dict_deep_merge( - src: _TDictLike, - dest: _TDictLike, - ctor: Optional[Callable[[], _TDictLike]] = None, -) -> _TDictLike: + src: _DictLike, + dest: _DictLike, + ctor: Optional[Callable[[], _DictLike]] = None, +) -> _DictLike: """Deep merge dict-like `src` into dict-like `dest`. `dest` is mutated in place and also returned. @@ -480,20 +480,20 @@ def all_files(cls) -> Generator['SettingsLevel', None, None]: # name of that settings file, without the extension. # TODO(chadnorvell): Would be great to constrain this to enums, but bound= # doesn't do what we want with Enum or EnumMeta. -_TSettingsType = TypeVar('_TSettingsType') +_SettingsTypeT = TypeVar('_SettingsTypeT') # Maps each settings type with the callback that generates the default settings # for that settings type. -EditorSettingsTypesWithDefaults = Dict[_TSettingsType, DefaultSettingsCallback] +EditorSettingsTypesWithDefaults = Dict[_SettingsTypeT, DefaultSettingsCallback] -class EditorSettingsManager(Generic[_TSettingsType]): +class EditorSettingsManager(Generic[_SettingsTypeT]): """Manages all settings for a particular editor. This is where you interact with an editor's settings (actually in a subclass of this class, not here). Initializing this class sets up access to one or more settings files for an editor (determined by - ``_TSettingsType``, fulfilled by an enum that defines each of an editor's + ``_SettingsTypeT``, fulfilled by an enum that defines each of an editor's settings files), along with the cascading settings levels. """ @@ -509,7 +509,7 @@ class EditorSettingsManager(Generic[_TSettingsType]): # These must be overridden in child classes. default_settings_dir: Path = None # type: ignore file_format: _StructuredFileFormat = _StructuredFileFormat() - types_with_defaults: EditorSettingsTypesWithDefaults[_TSettingsType] = {} + types_with_defaults: EditorSettingsTypesWithDefaults[_SettingsTypeT] = {} def __init__( self, @@ -517,7 +517,7 @@ def __init__( settings_dir: Optional[Path] = None, file_format: Optional[_StructuredFileFormat] = None, types_with_defaults: Optional[ - EditorSettingsTypesWithDefaults[_TSettingsType] + EditorSettingsTypesWithDefaults[_SettingsTypeT] ] = None, ): if SettingsLevel.ACTIVE in self.__class__.prefixes: @@ -564,7 +564,7 @@ def __init__( # each settings type. Those settings definitions may be stored in files # or not. self._settings_definitions: Dict[ - SettingsLevel, Dict[_TSettingsType, EditorSettingsDefinition] + SettingsLevel, Dict[_SettingsTypeT, EditorSettingsDefinition] ] = {} self._settings_types = tuple(self._types_with_defaults.keys()) @@ -597,19 +597,19 @@ def __init__( self._settings_dir, name, self._file_format ) - def default(self, settings_type: _TSettingsType): + def default(self, settings_type: _SettingsTypeT): """Default settings for the provided settings type.""" return self._settings_definitions[SettingsLevel.DEFAULT][settings_type] - def project(self, settings_type: _TSettingsType): + def project(self, settings_type: _SettingsTypeT): """Project settings for the provided settings type.""" return self._settings_definitions[SettingsLevel.PROJECT][settings_type] - def user(self, settings_type: _TSettingsType): + def user(self, settings_type: _SettingsTypeT): """User settings for the provided settings type.""" return self._settings_definitions[SettingsLevel.USER][settings_type] - def active(self, settings_type: _TSettingsType): + def active(self, settings_type: _SettingsTypeT): """Active settings for the provided settings type.""" return self._settings_definitions[SettingsLevel.ACTIVE][settings_type] diff --git a/pw_presubmit/py/pw_presubmit/build.py b/pw_presubmit/py/pw_presubmit/build.py index 167de408b..6ef59e880 100644 --- a/pw_presubmit/py/pw_presubmit/build.py +++ b/pw_presubmit/py/pw_presubmit/build.py @@ -520,7 +520,7 @@ def test_server(executable: str, output_dir: Path): yield finally: - proc.terminate() + proc.terminate() # pylint: disable=used-before-assignment @filter_paths( diff --git a/pw_presubmit/py/pw_presubmit/presubmit.py b/pw_presubmit/py/pw_presubmit/presubmit.py index bf0802087..61233d3e5 100644 --- a/pw_presubmit/py/pw_presubmit/presubmit.py +++ b/pw_presubmit/py/pw_presubmit/presubmit.py @@ -1296,7 +1296,7 @@ def _try_call( _LOG.warning('%s', failure) return PresubmitResult.FAIL - except Exception as failure: # pylint: disable=broad-except + except Exception as _failure: # pylint: disable=broad-except _LOG.exception('Presubmit check %s failed!', self.name) return PresubmitResult.FAIL diff --git a/pw_rpc/py/pw_rpc/callback_client/call.py b/pw_rpc/py/pw_rpc/callback_client/call.py index 0378aeb30..6cb09aaef 100644 --- a/pw_rpc/py/pw_rpc/callback_client/call.py +++ b/pw_rpc/py/pw_rpc/callback_client/call.py @@ -46,17 +46,17 @@ class UseDefault(enum.Enum): VALUE = 0 -CallType = TypeVar( - 'CallType', +CallTypeT = TypeVar( + 'CallTypeT', 'UnaryCall', 'ServerStreamingCall', 'ClientStreamingCall', 'BidirectionalStreamingCall', ) -OnNextCallback = Callable[[CallType, Any], Any] -OnCompletedCallback = Callable[[CallType, Any], Any] -OnErrorCallback = Callable[[CallType, Any], Any] +OnNextCallback = Callable[[CallTypeT, Any], Any] +OnCompletedCallback = Callable[[CallTypeT, Any], Any] +OnErrorCallback = Callable[[CallTypeT, Any], Any] OptionalTimeout = Union[UseDefault, float, None] diff --git a/pw_rpc/py/pw_rpc/callback_client/impl.py b/pw_rpc/py/pw_rpc/callback_client/impl.py index e74e3043a..474757373 100644 --- a/pw_rpc/py/pw_rpc/callback_client/impl.py +++ b/pw_rpc/py/pw_rpc/callback_client/impl.py @@ -28,7 +28,7 @@ from pw_rpc.callback_client.call import ( UseDefault, OptionalTimeout, - CallType, + CallTypeT, UnaryResponse, StreamResponse, Call, @@ -106,14 +106,14 @@ def help(self) -> str: def _start_call( self, - call_type: Type[CallType], + call_type: Type[CallTypeT], request: Optional[Message], timeout_s: OptionalTimeout, on_next: Optional[OnNextCallback], on_completed: Optional[OnCompletedCallback], on_error: Optional[OnErrorCallback], ignore_errors: bool = False, - ) -> CallType: + ) -> CallTypeT: """Creates the Call object and invokes the RPC using it.""" if timeout_s is UseDefault.VALUE: timeout_s = self.default_timeout_s @@ -125,8 +125,8 @@ def _start_call( return call def _client_streaming_call_type( - self, base: Type[CallType] - ) -> Type[CallType]: + self, base: Type[CallTypeT] + ) -> Type[CallTypeT]: """Creates a client or bidirectional stream call type. Applies the signature from the request protobuf to the send method. diff --git a/pw_rpc/py/pw_rpc/lossy_channel.py b/pw_rpc/py/pw_rpc/lossy_channel.py index feb46c1d4..db8908a07 100644 --- a/pw_rpc/py/pw_rpc/lossy_channel.py +++ b/pw_rpc/py/pw_rpc/lossy_channel.py @@ -112,27 +112,22 @@ def keep_packet(self) -> bool: def next_packet_duplicated(self) -> bool: return False - @staticmethod - def next_packet_out_of_order() -> bool: + def next_packet_out_of_order(self) -> bool: return False - @staticmethod - def next_packet_delayed() -> bool: + def next_packet_delayed(self) -> bool: return False def next_packet_dropped(self) -> bool: return not self.keep_packet() - @staticmethod - def next_packet_delay() -> int: + def next_packet_delay(self) -> int: return 0 - @staticmethod - def next_num_dupes() -> int: + def next_num_dupes(self) -> int: return 0 - @staticmethod - def choose_out_of_order_packet(max_idx) -> int: + def choose_out_of_order_packet(self, max_idx) -> int: return 0 diff --git a/pw_software_update/py/pw_software_update/generate_test_bundle.py b/pw_software_update/py/pw_software_update/generate_test_bundle.py index 0c52439df..14409ae7d 100644 --- a/pw_software_update/py/pw_software_update/generate_test_bundle.py +++ b/pw_software_update/py/pw_software_update/generate_test_bundle.py @@ -280,11 +280,11 @@ def parse_args(): return parser.parse_args() +# TODO(b/237580538): Refactor the code so that each test bundle generation +# is done in a separate function or script. +# pylint: disable=too-many-locals def main() -> int: """Main""" - # TODO(b/237580538): Refactor the code so that each test bundle generation - # is done in a separate function or script. - # pylint: disable=too-many-locals args = parse_args() test_bundle = Bundle() @@ -515,11 +515,13 @@ def main() -> int: ], check=True, ) - # TODO(b/237580538): Refactor the code so that each test bundle generation - # is done in a separate function or script. - # pylint: enable=too-many-locals return 0 +# TODO(b/237580538): Refactor the code so that each test bundle generation +# is done in a separate function or script. +# pylint: enable=too-many-locals + + if __name__ == "__main__": sys.exit(main()) diff --git a/pw_tokenizer/py/pw_tokenizer/database.py b/pw_tokenizer/py/pw_tokenizer/database.py index 093186873..26a32a7fa 100755 --- a/pw_tokenizer/py/pw_tokenizer/database.py +++ b/pw_tokenizer/py/pw_tokenizer/database.py @@ -594,7 +594,7 @@ def year_month_day(value) -> datetime: def replacement(value: str) -> Tuple[Pattern, 'str']: try: find, sub = unescaped_slash.split(value, 1) - except ValueError as err: + except ValueError as _err: raise argparse.ArgumentTypeError( 'replacements must be specified as "search_regex/replacement"' ) diff --git a/pw_unit_test/py/pw_unit_test/test_runner.py b/pw_unit_test/py/pw_unit_test/test_runner.py index bc88f9918..a7b06ac5e 100644 --- a/pw_unit_test/py/pw_unit_test/test_runner.py +++ b/pw_unit_test/py/pw_unit_test/test_runner.py @@ -306,6 +306,7 @@ def _maybe_upload_to_resultdb( % self._result_sink['auth_token'], }, data=json.dumps({'testResults': [test_result]}), + timeout=5.0, ).raise_for_status()