def test_pylintrc_parentdir() -> None: """Test that the first pylintrc we find is the first parent directory.""" with tempdir() as chroot: chroot_path = Path(chroot) testutils.create_files( [ "a/pylintrc", "a/b/__init__.py", "a/b/pylintrc", "a/b/c/__init__.py", "a/b/c/d/__init__.py", "a/b/c/d/e/.pylintrc", ] ) with fake_home(): assert not list(config.find_default_config_files()) results = { "a": chroot_path / "a" / "pylintrc", "a/b": chroot_path / "a" / "b" / "pylintrc", "a/b/c": chroot_path / "a" / "b" / "pylintrc", "a/b/c/d": chroot_path / "a" / "b" / "pylintrc", "a/b/c/d/e": chroot_path / "a" / "b" / "c" / "d" / "e" / ".pylintrc", } for basedir, expected in results.items(): os.chdir(chroot_path / basedir) assert next(config.find_default_config_files()) == expected
def test_pylintrc() -> None: """Test that the environment variable is checked for existence.""" with fake_home(): current_dir = os.getcwd() os.chdir(os.path.dirname(os.path.abspath(sys.executable))) try: assert not list(config.find_default_config_files()) os.environ["PYLINTRC"] = os.path.join(tempfile.gettempdir(), ".pylintrc") assert not list(config.find_default_config_files()) os.environ["PYLINTRC"] = "." assert not list(config.find_default_config_files()) finally: os.chdir(current_dir) importlib.reload(config)
def __init__(self, test_file: Tuple[str, Path]) -> None: self._test_file = test_file _test_reporter = FunctionalTestReporter() self._linter = PyLinter() self._linter.namespace.persistent = 0 checkers.initialize(self._linter) # Check if this message has a custom configuration file (e.g. for enabling optional checkers). # If not, use the default configuration. config_file: Optional[Path] if (test_file[1].parent / "pylintrc").exists(): config_file = test_file[1].parent / "pylintrc" else: config_file = next(config.find_default_config_files(), None) _config_initialization( self._linter, args_list=[ str(test_file[1]), "--disable=all", f"--enable={test_file[0]}", ], reporter=_test_reporter, config_file=config_file, )
def __init__(self, test_file: Tuple[str, Path]) -> None: self._test_file = test_file _test_reporter = FunctionalTestReporter() self._linter = PyLinter() self._linter.config.persistent = 0 checkers.initialize(self._linter) # Check if this message has a custom configuration file (e.g. for enabling optional checkers). # If not, use the default configuration. config_file: Optional[Path] msgid, full_path = test_file pylintrc = full_path.parent / "pylintrc" if pylintrc.exists(): config_file = pylintrc else: config_file = next(config.find_default_config_files(), None) args = [ str(full_path), "--disable=all", f"--enable={msgid},astroid-error,fatal,syntax-error", ] print(f"Command used:\npylint {' '.join(args)}") _config_initialization( self._linter, args_list=args, reporter=_test_reporter, config_file=config_file, )
def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): # some stuff has to be done before ancestors initialization... # # messages store / checkers / reporter / astroid manager self.msgs_store = MessageDefinitionStore() self.reporter = None self._reporter_name = None self._reporters = {} self._checkers = collections.defaultdict(list) self._pragma_lineno = {} self._ignore_file = False # visit variables self.file_state = FileState() self.current_name = None self.current_file = None self.stats = None # init options self._external_opts = options self.options = options + PyLinter.make_options() self.option_groups = option_groups + PyLinter.option_groups self._options_methods = { "enable": self.enable, "disable": self.disable } self._bw_options_methods = { "disable-msg": self.disable, "enable-msg": self.enable, } MessagesHandlerMixIn.__init__(self) reporters.ReportsHandlerMixIn.__init__(self) super().__init__( usage=__doc__, config_file=pylintrc or next(config.find_default_config_files(), None), ) checkers.BaseTokenChecker.__init__(self) # provided reports self.reports = ( ("RP0001", "Messages by category", report_total_messages_stats), ( "RP0002", "% errors / warnings by module", report_messages_by_module_stats, ), ("RP0003", "Messages", report_messages_stats), ) self.register_checker(self) self._dynamic_plugins = set() self._python3_porting_mode = False self._error_mode = False self.load_provider_defaults() if reporter: self.set_reporter(reporter)
def test_non_existent_home() -> None: """Test that we handle a non-existent home directory. Reported in https://github.com/PyCQA/pylint/issues/6802. """ with mock.patch("pathlib.Path.home", side_effect=RuntimeError): current_dir = os.getcwd() os.chdir(os.path.dirname(os.path.abspath(sys.executable))) assert not list(config.find_default_config_files()) os.chdir(current_dir)
def pytest_configure(self, config): """Configure pytest after it is already enabled""" # Find pylintrc to check ignore list if config.option.pylint_rcfile: pylintrc_file = config.option.pylint_rcfile else: # handling files apart from pylintrc was only introduced in pylint # 2.5, if we can't use find_default_config_files(), fall back on PYLINTRC # once we drop support below 2.5 we can get rid of this try: pylintrc_file = next(pylint_config.find_default_config_files(), None) except AttributeError: pylintrc_file = pylint_config.PYLINTRC if pylintrc_file and not exists(pylintrc_file): # The directory of pytest.ini got a chance pylintrc_file = join(dirname(str(config.inifile)), pylintrc_file) # Try getting ignores from pylintrc since we use pytest # collection methods and not pylint's internal mechanism if pylintrc_file and exists(pylintrc_file): self.pylintrc_file = pylintrc_file # Check if pylint config has a different filename or date # and invalidate the cache if it has changed. pylint_mtime = getmtime(pylintrc_file) cache_key = PYLINT_CONFIG_CACHE_KEY + pylintrc_file cache_value = self.mtimes.get(cache_key) if cache_value is None or cache_value < pylint_mtime: self.mtimes = {} self.mtimes[cache_key] = pylint_mtime if pylintrc_file.endswith(".toml"): self._load_pyproject_toml(pylintrc_file) else: self._load_rc_file(pylintrc_file) # Command line arguments take presedence over rcfile ones if set if config.option.pylint_ignore is not None: self.pylint_ignore = config.option.pylint_ignore.split(",") if config.option.pylint_ignore_patterns is not None: self.pylint_ignore_patterns = config.option.pylint_ignore_patterns.split( ",")
def test_pylintrc_parentdir_no_package() -> None: """Test that we don't find a pylintrc in sub-packages.""" with tempdir() as chroot: with fake_home(): chroot_path = Path(chroot) testutils.create_files( ["a/pylintrc", "a/b/pylintrc", "a/b/c/d/__init__.py"] ) assert config.find_pylintrc() is None results = { "a": chroot_path / "a" / "pylintrc", "a/b": chroot_path / "a" / "b" / "pylintrc", "a/b/c": None, "a/b/c/d": None, } for basedir, expected in results.items(): os.chdir(chroot_path / basedir) assert next(config.find_default_config_files(), None) == expected
def __init__( self, args: Sequence[str], reporter: BaseReporter | None = None, exit: bool = True, # pylint: disable=redefined-builtin do_exit: Any = UNUSED_PARAM_SENTINEL, ) -> None: # Immediately exit if user asks for version if "--version" in args: print(full_version) sys.exit(0) self._rcfile: str | None = None self._output: str | None = None self._plugins: list[str] = [] self.verbose: bool = False # Pre-process certain options and remove them from args list try: args = _preprocess_options(self, args) except ArgumentPreprocessingError as ex: print(ex, file=sys.stderr) sys.exit(32) # Determine configuration file if self._rcfile is None: default_file = next(config.find_default_config_files(), None) if default_file: self._rcfile = str(default_file) self.linter = linter = self.LinterClass( _make_run_options(self), option_groups=self.option_groups, pylintrc=self._rcfile, ) # register standard checkers linter.load_default_plugins() # load command line plugins linter.load_plugin_modules(self._plugins) linter.disable("I") linter.enable("c-extension-no-member") # Register the options needed for 'pylint-config' # By not registering them by default they don't show up in the normal usage message if self._is_pylint_config: _register_generate_config_options(linter._arg_parser) args = _config_initialization(linter, args, reporter, config_file=self._rcfile, verbose_mode=self.verbose) # Handle the 'pylint-config' command if self._is_pylint_config: warnings.warn( "NOTE: The 'pylint-config' command is experimental and usage can change", UserWarning, ) code = _handle_pylint_config_commands(linter) if exit: sys.exit(code) return # Display help messages if there are no files to lint if not args: print(linter.help()) sys.exit(32) if linter.config.jobs < 0: print( f"Jobs number ({linter.config.jobs}) should be greater than or equal to 0", file=sys.stderr, ) sys.exit(32) if linter.config.jobs > 1 or linter.config.jobs == 0: if multiprocessing is None: print( "Multiprocessing library is missing, fallback to single process", file=sys.stderr, ) linter.set_option("jobs", 1) elif linter.config.jobs == 0: linter.config.jobs = _cpu_count() if self._output: try: with open(self._output, "w", encoding="utf-8") as output: linter.reporter.out = output linter.check(args) score_value = linter.generate_reports() except OSError as ex: print(ex, file=sys.stderr) sys.exit(32) else: linter.check(args) score_value = linter.generate_reports() if do_exit is not UNUSED_PARAM_SENTINEL: warnings.warn( "do_exit is deprecated and it is going to be removed in a future version.", DeprecationWarning, ) exit = do_exit if exit: if linter.config.exit_zero: sys.exit(0) elif linter.any_fail_on_issues(): # We need to make sure we return a failing exit code in this case. # So we use self.linter.msg_status if that is non-zero, otherwise we just return 1. sys.exit(self.linter.msg_status or 1) elif score_value is not None: if score_value >= linter.config.fail_under: sys.exit(0) else: # We need to make sure we return a failing exit code in this case. # So we use self.linter.msg_status if that is non-zero, otherwise we just return 1. sys.exit(self.linter.msg_status or 1) else: sys.exit(self.linter.msg_status)
def __init__( self, args, reporter=None, exit=True, do_exit=UNUSED_PARAM_SENTINEL, ): # pylint: disable=redefined-builtin # Immediately exit if user asks for version if "--version" in args: print(full_version) sys.exit(0) self._rcfile: Optional[str] = None self._output: Optional[str] = None self._plugins: List[str] = [] self.verbose: bool = False # Preprocess certain options and remove them from args list try: args = _preprocess_options(self, args) except ArgumentPreprocessingError as ex: print(ex, file=sys.stderr) sys.exit(32) # Determine configuration file if self._rcfile is None: self._rcfile = next(config.find_default_config_files(), None) self.linter = linter = self.LinterClass( ( ( "rcfile", { "action": _DoNothingAction, "kwargs": {}, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Specify a configuration file to load.", }, ), ( "output", { "action": _DoNothingAction, "kwargs": {}, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Specify an output file.", }, ), ( "init-hook", { "action": _DoNothingAction, "kwargs": {}, "callback": Run._not_implemented_callback, "help": "Python code to execute, usually for sys.path " "manipulation such as pygtk.require().", }, ), ( "help-msg", { "action": _MessageHelpAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Display a help message for the given message id and " "exit. The value may be a comma separated list of message ids.", }, ), ( "list-msgs", { "action": _ListMessagesAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Display a list of all pylint's messages divided by whether " "they are emittable with the given interpreter.", }, ), ( "list-msgs-enabled", { "action": _ListMessagesEnabledAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Display a list of what messages are enabled, " "disabled and non-emittable with the given configuration.", }, ), ( "list-groups", { "action": _ListCheckGroupsAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "List pylint's message groups.", }, ), ( "list-conf-levels", { "action": _ListConfidenceLevelsAction, "callback": Run._not_implemented_callback, "kwargs": { "Run": self }, "group": "Commands", "help": "Generate pylint's confidence levels.", }, ), ( "list-extensions", { "action": _ListExtensionsAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "List available extensions.", }, ), ( "full-documentation", { "action": _FullDocumentationAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Generate pylint's full documentation.", }, ), ( "generate-rcfile", { "action": _GenerateRCFileAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "group": "Commands", "help": "Generate a sample configuration file according to " "the current configuration. You can put other options " "before this one to get them in the generated " "configuration.", }, ), ( "errors-only", { "action": _ErrorsOnlyModeAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "short": "E", "help": "In error mode, checkers without error messages are " "disabled and for others, only the ERROR messages are " "displayed, and no reports are done by default.", }, ), ( "verbose", { "action": _DoNothingAction, "kwargs": {}, "callback": Run._not_implemented_callback, "short": "v", "help": "In verbose mode, extra non-checker-related info " "will be displayed.", }, ), ( "enable-all-extensions", { "action": _DoNothingAction, "kwargs": {}, "callback": Run._not_implemented_callback, "help": "Load and enable all available extensions. " "Use --list-extensions to see a list all available extensions.", }, ), ( "long-help", { "action": _LongHelpAction, "kwargs": { "Run": self }, "callback": Run._not_implemented_callback, "help": "Show more verbose help.", "group": "Commands", }, ), ), option_groups=self.option_groups, pylintrc=self._rcfile, ) # register standard checkers linter.load_default_plugins() # load command line plugins linter.load_plugin_modules(self._plugins) linter.disable("I") linter.enable("c-extension-no-member") args = _config_initialization(linter, args, reporter, config_file=self._rcfile, verbose_mode=self.verbose) if linter.namespace.jobs < 0: print( f"Jobs number ({linter.namespace.jobs}) should be greater than or equal to 0", file=sys.stderr, ) sys.exit(32) if linter.namespace.jobs > 1 or linter.namespace.jobs == 0: if multiprocessing is None: print( "Multiprocessing library is missing, fallback to single process", file=sys.stderr, ) linter.set_option("jobs", 1) elif linter.namespace.jobs == 0: linter.namespace.jobs = _cpu_count() if self._output: try: with open(self._output, "w", encoding="utf-8") as output: linter.reporter.out = output linter.check(args) score_value = linter.generate_reports() except OSError as ex: print(ex, file=sys.stderr) sys.exit(32) else: linter.check(args) score_value = linter.generate_reports() if do_exit is not UNUSED_PARAM_SENTINEL: warnings.warn( "do_exit is deprecated and it is going to be removed in a future version.", DeprecationWarning, ) exit = do_exit if exit: if linter.namespace.exit_zero: sys.exit(0) elif linter.any_fail_on_issues(): # We need to make sure we return a failing exit code in this case. # So we use self.linter.msg_status if that is non-zero, otherwise we just return 1. sys.exit(self.linter.msg_status or 1) elif score_value is not None: if score_value >= linter.namespace.fail_under: sys.exit(0) else: # We need to make sure we return a failing exit code in this case. # So we use self.linter.msg_status if that is non-zero, otherwise we just return 1. sys.exit(self.linter.msg_status or 1) else: sys.exit(self.linter.msg_status)