Skip to content

Commit 4de3d2d

Browse files
authored
Merge pull request #42 from hetmankp/master
Create new config option 'config_sub_paths'
2 parents a4e3750 + be14272 commit 4de3d2d

File tree

3 files changed

+76
-25
lines changed

3 files changed

+76
-25
lines changed

README.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ Configuration
3939
``dmypy_status_file`` (Default is ``.dmypy.json``) specifies which status file dmypy should use.
4040
This modifies the ``--status-file`` option passed to ``dmypy`` given ``dmypy`` is active.
4141

42+
``config_sub_paths`` (default is ``[]``) specifies sub paths under which the mypy configuration file may be found.
43+
For each directory searched for the mypy config file, this also searches the sub paths specified here
44+
4245
This project supports the use of ``pyproject.toml`` for configuration. It is in fact the preferred way. Using that your configuration could look like this:
4346

4447
::
@@ -91,6 +94,15 @@ With ``dmypy_status_file`` your config could look like this:
9194
"dmypy_status_file": ".custom_dmypy_status_file.json"
9295
}
9396

97+
With ``config_sub_paths`` your config could look like this:
98+
99+
::
100+
101+
{
102+
"enabled": True,
103+
"config_sub_paths": [".config"]
104+
}
105+
94106
Developing
95107
-------------
96108

pylsp_mypy/plugin.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def init(workspace: str) -> Dict[str, str]:
358358

359359
configuration = {}
360360
path = findConfigFile(
361-
workspace, ["pylsp-mypy.cfg", "mypy-ls.cfg", "mypy_ls.cfg", "pyproject.toml"], False
361+
workspace, [], ["pylsp-mypy.cfg", "mypy-ls.cfg", "mypy_ls.cfg", "pyproject.toml"], False
362362
)
363363
if path:
364364
if "pyproject.toml" in path:
@@ -367,16 +367,19 @@ def init(workspace: str) -> Dict[str, str]:
367367
with open(path) as file:
368368
configuration = ast.literal_eval(file.read())
369369

370+
configSubPaths = configuration.get("config_sub_paths", [])
370371
mypyConfigFile = findConfigFile(
371-
workspace, ["mypy.ini", ".mypy.ini", "pyproject.toml", "setup.cfg"], True
372+
workspace, configSubPaths, ["mypy.ini", ".mypy.ini", "pyproject.toml", "setup.cfg"], True
372373
)
373374
mypyConfigFileMap[workspace] = mypyConfigFile
374375

375376
log.info("mypyConfigFile = %s configuration = %s", mypyConfigFile, configuration)
376377
return configuration
377378

378379

379-
def findConfigFile(path: str, names: List[str], mypy: bool) -> Optional[str]:
380+
def findConfigFile(
381+
path: str, configSubPaths: List[str], names: List[str], mypy: bool
382+
) -> Optional[str]:
380383
"""
381384
Search for a config file.
382385
@@ -387,6 +390,8 @@ def findConfigFile(path: str, names: List[str], mypy: bool) -> Optional[str]:
387390
----------
388391
path : str
389392
The path where the search starts.
393+
configSubPaths : List[str]
394+
Additional sub search paths in which mypy configs might be located
390395
names : List[str]
391396
The file to be found (or alternative names).
392397
mypy : bool
@@ -401,26 +406,28 @@ def findConfigFile(path: str, names: List[str], mypy: bool) -> Optional[str]:
401406
start = Path(path).joinpath(names[0]) # the join causes the parents to include path
402407
for parent in start.parents:
403408
for name in names:
404-
file = parent.joinpath(name)
405-
if file.is_file():
406-
if file.name in ["mypy-ls.cfg", "mypy_ls.cfg"]:
407-
raise NameError(
408-
f"{str(file)}: {file.name} is no longer supported, you should rename your "
409-
"config file to pylsp-mypy.cfg or preferably use a pyproject.toml instead."
410-
)
411-
if file.name == "pyproject.toml":
412-
configPresent = (
413-
toml.load(file).get("tool", {}).get("mypy" if mypy else "pylsp-mypy")
414-
is not None
415-
)
416-
if not configPresent:
417-
continue
418-
if file.name == "setup.cfg":
419-
config = ConfigParser()
420-
config.read(str(file))
421-
if "mypy" not in config:
422-
continue
423-
return str(file)
409+
for subPath in [""] + configSubPaths:
410+
file = parent.joinpath(subPath).joinpath(name)
411+
if file.is_file():
412+
if file.name in ["mypy-ls.cfg", "mypy_ls.cfg"]:
413+
raise NameError(
414+
f"{str(file)}: {file.name} is no longer supported, you should rename "
415+
"your config file to pylsp-mypy.cfg or preferably use a pyproject.toml "
416+
"instead."
417+
)
418+
if file.name == "pyproject.toml":
419+
configPresent = (
420+
toml.load(file).get("tool", {}).get("mypy" if mypy else "pylsp-mypy")
421+
is not None
422+
)
423+
if not configPresent:
424+
continue
425+
if file.name == "setup.cfg":
426+
config = ConfigParser()
427+
config.read(str(file))
428+
if "mypy" not in config:
429+
continue
430+
return str(file)
424431
# No config file found in the whole directory tree
425432
# -> check mypy default locations for mypy config
426433
if mypy:

test/test_plugin.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from pylsp_mypy import plugin
1414

15+
# TODO using these file as a document is a bad idea as tests can break by adding new tests
1516
DOC_URI = f"file:/{Path(__file__)}"
1617
DOC_TYPE_ERR = """{}.append(3)
1718
"""
@@ -69,8 +70,7 @@ def test_plugin(workspace, last_diagnostics_monkeypatch):
6970

7071

7172
def test_parse_full_line(workspace):
72-
doc = Document(DOC_URI, workspace)
73-
diag = plugin.parse_line(TEST_LINE, doc)
73+
diag = plugin.parse_line(TEST_LINE) # TODO parse a document here
7474
assert diag["message"] == '"Request" has no attribute "id"'
7575
assert diag["range"]["start"] == {"line": 278, "character": 7}
7676
assert diag["range"]["end"] == {"line": 278, "character": 8}
@@ -253,3 +253,35 @@ def test_dmypy_status_file(tmpdir, last_diagnostics_monkeypatch, workspace):
253253
)
254254

255255
assert statusFile.exists()
256+
257+
258+
def test_config_sub_paths(tmpdir, last_diagnostics_monkeypatch):
259+
DOC_SOURCE = """
260+
def foo():
261+
return
262+
unreachable = 1
263+
"""
264+
DOC_ERR_MSG = "Statement is unreachable [unreachable]"
265+
266+
config_sub_paths = [".config"]
267+
268+
# Initialize workspace.
269+
ws = Workspace(uris.from_fs_path(str(tmpdir)), Mock())
270+
ws._config = Config(ws.root_uri, {}, 0, {})
271+
272+
# Create configuration file for workspace.
273+
plugin_config = tmpdir.join("pyproject.toml")
274+
plugin_config.write(f"[tool.pylsp-mypy]\nenabled = true\nconfig_sub_paths = {config_sub_paths}")
275+
config_dir = tmpdir.mkdir(".config")
276+
mypy_config = config_dir.join("mypy.ini")
277+
mypy_config.write("[mypy]\nwarn_unreachable = True\ncheck_untyped_defs = True")
278+
279+
# Update settings for workspace.
280+
plugin.pylsp_settings(ws._config)
281+
282+
# Test document to make sure it uses .config/mypy.ini configuration.
283+
doc = Document(DOC_URI, ws, DOC_SOURCE)
284+
diags = plugin.pylsp_lint(ws._config, ws, doc, is_saved=False)
285+
assert len(diags) == 1
286+
diag = diags[0]
287+
assert diag["message"] == DOC_ERR_MSG

0 commit comments

Comments
 (0)