From 957c334d1d77980d343bbfd38bb5d5f821454433 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 31 Aug 2025 11:42:30 -0500 Subject: [PATCH] fix: fix 'attribute-defined-outside-init' for pylint --- .pylintrc | 1 - src/wuttjamaican/app.py | 7 ++++--- src/wuttjamaican/conf.py | 3 ++- src/wuttjamaican/email.py | 8 ++++---- src/wuttjamaican/install.py | 35 +++++++++++++++++------------------ src/wuttjamaican/reports.py | 8 ++++---- tests/test_conf.py | 20 ++------------------ tests/test_install.py | 10 +--------- 8 files changed, 34 insertions(+), 58 deletions(-) diff --git a/.pylintrc b/.pylintrc index bd7e6f3..a8bc68a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,6 +2,5 @@ [MESSAGES CONTROL] disable= - attribute-defined-outside-init, fixme, too-many-branches, diff --git a/src/wuttjamaican/app.py b/src/wuttjamaican/app.py index 5bbe7ce..c10e6bc 100644 --- a/src/wuttjamaican/app.py +++ b/src/wuttjamaican/app.py @@ -143,7 +143,7 @@ class AppHandler: # pylint: disable=too-many-public-methods return self.get_enum() if name == "providers": - self.providers = self.get_all_providers() + self.__dict__["providers"] = self.get_all_providers() return self.providers for provider in self.providers.values(): @@ -340,7 +340,7 @@ class AppHandler: # pylint: disable=too-many-public-methods usedb=False, default=self.default_model_spec, ) - self.model = importlib.import_module(spec) + self.__dict__["model"] = importlib.import_module(spec) return self.model def get_enum(self): @@ -364,7 +364,7 @@ class AppHandler: # pylint: disable=too-many-public-methods spec = self.config.get( f"{self.appname}.enum_spec", usedb=False, default=self.default_enum_spec ) - self.enum = importlib.import_module(spec) + self.__dict__["enum"] = importlib.import_module(spec) return self.enum def load_object(self, spec): @@ -1154,6 +1154,7 @@ class GenericHandler: self.config = config self.app = self.config.get_app() self.modules = {} + self.classes = {} @property def appname(self): diff --git a/src/wuttjamaican/conf.py b/src/wuttjamaican/conf.py index fc8ed5c..b30a274 100644 --- a/src/wuttjamaican/conf.py +++ b/src/wuttjamaican/conf.py @@ -189,6 +189,7 @@ class WuttaConfig: # pylint: disable=too-many-instance-attributes See also :ref:`where-config-settings-come-from`. """ + _app = None default_app_handler_spec = "wuttjamaican.app:AppHandler" default_engine_maker_spec = "wuttjamaican.db.conf:make_engine_from_config" @@ -640,7 +641,7 @@ class WuttaConfig: # pylint: disable=too-many-instance-attributes See also :doc:`/narr/handlers/app`. """ - if not hasattr(self, "_app"): + if not self._app: spec = self.get( f"{self.appname}.app.handler", usedb=False, diff --git a/src/wuttjamaican/email.py b/src/wuttjamaican/email.py index 8d398c5..ab4040c 100644 --- a/src/wuttjamaican/email.py +++ b/src/wuttjamaican/email.py @@ -323,8 +323,8 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods This calls :meth:`get_email_modules()` and for each module, it discovers all the email settings it contains. """ - if not hasattr(self, "_email_settings"): - self._email_settings = {} + if "email_settings" not in self.classes: + self.classes["email_settings"] = {} for module in self.get_email_modules(): for name in dir(module): obj = getattr(module, name) @@ -333,9 +333,9 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods and obj is not EmailSetting and issubclass(obj, EmailSetting) ): - self._email_settings[obj.__name__] = obj + self.classes["email_settings"][obj.__name__] = obj - return self._email_settings + return self.classes["email_settings"] def get_email_setting(self, key, instance=True): """ diff --git a/src/wuttjamaican/install.py b/src/wuttjamaican/install.py index 754b43a..5077644 100644 --- a/src/wuttjamaican/install.py +++ b/src/wuttjamaican/install.py @@ -85,6 +85,7 @@ class InstallHandler(GenericHandler): app_title = None pypi_name = None egg_name = None + schema_installed = False def __init__(self, config, **kwargs): super().__init__(config) @@ -100,24 +101,6 @@ class InstallHandler(GenericHandler): if not self.egg_name: self.egg_name = self.pypi_name.replace("-", "_") - def run(self): - """ - Run the interactive command-line installer. - - This does the following: - - * check for ``prompt_toolkit`` and maybe ask to install it - * define the template lookup paths, for making config files - * call :meth:`show_welcome()` - * call :meth:`sanity_check()` - * call :meth:`do_install_steps()` - * call :meth:`show_goodbye()` - - Although if a problem is encountered then not all calls may - happen. - """ - self.require_prompt_toolkit() - paths = [ self.app.resource_path("wuttjamaican:templates/install"), ] @@ -131,6 +114,22 @@ class InstallHandler(GenericHandler): self.templates = TemplateLookup(directories=paths) + def run(self): + """ + Run the interactive command-line installer. + + This does the following: + + * check for ``prompt_toolkit`` and maybe ask to install it + * call :meth:`show_welcome()` + * call :meth:`sanity_check()` + * call :meth:`do_install_steps()` + * call :meth:`show_goodbye()` + + Although if a problem is encountered then not all calls may + happen. + """ + self.require_prompt_toolkit() self.show_welcome() self.sanity_check() self.schema_installed = False diff --git a/src/wuttjamaican/reports.py b/src/wuttjamaican/reports.py index 136ffa0..92b5705 100644 --- a/src/wuttjamaican/reports.py +++ b/src/wuttjamaican/reports.py @@ -157,8 +157,8 @@ class ReportHandler(GenericHandler): This calls :meth:`get_report_modules()` and for each module, it discovers all the reports it contains. """ - if not hasattr(self, "_reports"): - self._reports = {} + if "reports" not in self.classes: + self.classes["reports"] = {} for module in self.get_report_modules(): for name in dir(module): obj = getattr(module, name) @@ -167,9 +167,9 @@ class ReportHandler(GenericHandler): and obj is not Report and issubclass(obj, Report) ): - self._reports[obj.report_key] = obj + self.classes["reports"][obj.report_key] = obj - return self._reports + return self.classes["reports"] def get_report(self, key, instance=True): """ diff --git a/tests/test_conf.py b/tests/test_conf.py index 4ec5d97..18953b3 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -17,7 +17,6 @@ from wuttjamaican.testing import FileTestCase, ConfigTestCase class TestWuttaConfig(FileTestCase): - def make_config(self, **kwargs): return mod.WuttaConfig(**kwargs) @@ -291,7 +290,6 @@ configure_logging = true ) with patch.object(conf.WuttaConfig, "_configure_logging") as method: - # no logging config by default config = conf.WuttaConfig() method.assert_not_called() @@ -319,7 +317,6 @@ configure_logging = true ) with patch("wuttjamaican.conf.logging") as logging: - # basic constructor attempts logging config config = conf.WuttaConfig(configure_logging=True) logging.config.fileConfig.assert_called_once() @@ -338,14 +335,14 @@ configure_logging = true logging.config.fileConfig.assert_called_once() def test_config_has_no_app_after_init(self): - # initial config should *not* have an app yet, otherwise # extensions cannot specify a default app handler config = conf.WuttaConfig() - self.assertFalse(hasattr(config, "_app")) + self.assertIsNone(config._app) # but after that we can get an app okay app = config.get_app() + self.assertIsNotNone(app) self.assertIs(app, config._app) def test_setdefault(self): @@ -628,7 +625,6 @@ configure_logging = true self.assertEqual(value[2], "baz") def test_get_app(self): - # default handler config = conf.WuttaConfig() self.assertEqual(config.default_app_handler_spec, "wuttjamaican.app:AppHandler") @@ -686,7 +682,6 @@ def custom_make_engine_from_config(): class TestWuttaConfigExtension(TestCase): - def test_basic(self): config = conf.WuttaConfig() ext = conf.WuttaConfigExtension() @@ -695,7 +690,6 @@ class TestWuttaConfigExtension(TestCase): class TestGenericDefaultFiles(TestCase): - def test_linux(self): files = conf.generic_default_files("wuttatest") self.assertIsInstance(files, list) @@ -707,7 +701,6 @@ class TestGenericDefaultFiles(TestCase): win32com.shell.SHGetSpecialFolderPath.return_value = r"C:" + os.sep with patch.dict("sys.modules", **{"win32com.shell": win32com}): with patch("wuttjamaican.conf.sys", platform="win32"): - files = conf.generic_default_files("wuttatest") self.assertIsInstance(files, list) self.assertTrue(len(files) > 1) @@ -723,14 +716,12 @@ class TestGenericDefaultFiles(TestCase): with patch("builtins.__import__", side_effect=mock_import): with patch("wuttjamaican.conf.sys", platform="win32"): - files = conf.generic_default_files("wuttatest") self.assertIsInstance(files, list) self.assertEqual(len(files), 0) class TestGetConfigPaths(FileTestCase): - def test_winsvc(self): myconf = self.write_file( "my.conf", @@ -753,7 +744,6 @@ winsvc.RattailFileMonitor = /path/to/other/file class TestMakeConfig(FileTestCase): - # nb. we use appname='wuttatest' in this suite to avoid any # "valid" default config files, env vars etc. which may be present # on the dev machine @@ -763,7 +753,6 @@ class TestMakeConfig(FileTestCase): with patch("wuttjamaican.conf.generic_default_files") as generic_default_files: with patch("wuttjamaican.conf.WuttaConfig") as WuttaConfig: - # generic files are used if nothing is specified generic_default_files.return_value = [generic] config = conf.make_config(appname="wuttatest") @@ -788,7 +777,6 @@ class TestMakeConfig(FileTestCase): with patch("wuttjamaican.conf.generic_default_files") as generic_default_files: with patch("wuttjamaican.conf.WuttaConfig") as WuttaConfig: - # generic defaults are used if nothing specified generic_default_files.return_value = [generic] config = conf.make_config(appname="wuttatest") @@ -832,7 +820,6 @@ class TestMakeConfig(FileTestCase): with patch("wuttjamaican.conf.generic_default_files") as generic_default_files: with patch("wuttjamaican.conf.WuttaConfig") as WuttaConfig: - generic_default_files.return_value = [generic] # no plus files by default @@ -877,7 +864,6 @@ class TestMakeConfig(FileTestCase): with patch("wuttjamaican.conf.generic_default_files") as generic_default_files: with patch("wuttjamaican.conf.WuttaConfig") as WuttaConfig: - generic_default_files.return_value = [generic] # generic files by default @@ -922,7 +908,6 @@ class TestMakeConfig(FileTestCase): with patch.object(mod, "WuttaConfig") as WuttaConfig: with patch.object(mod, "load_entry_points") as load_entry_points: - # no entry points loaded if extend=False config = mod.make_config(appname="wuttatest", extend=False) WuttaConfig.assert_called_once_with( @@ -967,7 +952,6 @@ class TestMakeConfig(FileTestCase): class TestWuttaConfigProfile(ConfigTestCase): - def make_profile(self, key): return mod.WuttaConfigProfile(self.config, key) diff --git a/tests/test_install.py b/tests/test_install.py index f05ffea..a58725f 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -12,7 +12,6 @@ from wuttjamaican.testing import ConfigTestCase class TestInstallHandler(ConfigTestCase): - def make_handler(self, **kwargs): return mod.InstallHandler(self.config, **kwargs) @@ -38,7 +37,6 @@ class TestInstallHandler(ConfigTestCase): with patch.object(mod, "sys") as sys: with patch.object(handler, "rprint") as rprint: with patch.object(handler, "prompt_bool") as prompt_bool: - # user continues prompt_bool.return_value = True handler.show_welcome() @@ -54,7 +52,6 @@ class TestInstallHandler(ConfigTestCase): with patch.object(mod, "sys") as sys: with patch.object(mod, "os") as os: with patch.object(handler, "rprint") as rprint: - # pretend appdir does not exist os.path.exists.return_value = False handler.sanity_check() @@ -79,10 +76,9 @@ class TestInstallHandler(ConfigTestCase): with patch.object(handler, "get_dbinfo", return_value=dbinfo): with patch.object(handler, "make_appdir") as make_appdir: with patch.object(handler, "install_db_schema") as install_db_schema: - # nb. just for sanity/coverage install_db_schema.return_value = True - self.assertFalse(hasattr(handler, "schema_installed")) + self.assertFalse(handler.schema_installed) handler.do_install_steps() self.assertTrue(make_appdir.called) self.assertTrue(handler.schema_installed) @@ -109,7 +105,6 @@ class TestInstallHandler(ConfigTestCase): with patch.object(handler, "prompt_generic", side_effect=prompt_generic): with patch.object(handler, "test_db_connection") as test_db_connection: with patch.object(handler, "rprint") as rprint: - # bad dbinfo test_db_connection.return_value = "bad dbinfo" sys.exit.side_effect = RuntimeError @@ -220,7 +215,6 @@ default.url = {db_url} db_url = sa.create_engine(db_url).url with patch.object(mod, "subprocess") as subprocess: - # user declines offer to install schema with patch.object(handler, "prompt_bool", return_value=False): self.assertFalse(handler.install_db_schema(db_url, appdir=self.tempdir)) @@ -321,7 +315,6 @@ default.url = {db_url} with patch("builtins.__import__", side_effect=mock_import): with patch.object(handler, "get_prompt_style", return_value=style): with patch.object(handler, "rprint") as rprint: - # no input or default value mock_prompt.return_value = "" result = handler.prompt_generic("foo") @@ -452,7 +445,6 @@ default.url = {db_url} with patch("builtins.__import__", side_effect=mock_import): with patch.object(handler, "rprint") as rprint: - # no default; true input mock_prompt.reset_mock() mock_prompt.return_value = "Y"