Repository: facebookexperimental/usort
Pull request #198 (2022-09-01 23:58:55)

Bump sphinx-mdinclude from 0.5.1 to 0.5.2
Bumps [sphinx-mdinclude](https://github.com/amyreese/sphinx-mdinclude) from 0.5.1 to 0.5.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/amyreese/sphinx-mdinclude/blob/main/CHANGELOG.md">sphinx-mdinclude's changelog</a>.</em></p>
<blockquote>
<h2>v0.5.2</h2>
<p>Maintenance release</p>
<ul>
<li>Add support for docutils <code>0.19</code> (<a href="https://github-redirect.dependabot.com/amyreese/sphinx-mdinclude/issues/8">#8</a>)</li>
<li>Enable building docs without installing package (<a href="https://github-redirect.dependabot.com/amyreese/sphinx-mdinclude/issues/6">#6</a>)</li>
</ul>
<pre><code>$ git shortlog -s v0.5.1...v0.5.2
     6	Amethyst Reese
</code></pre>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/amyreese/sphinx-mdinclude/commit/f9495dac524da1eac55c49cc6874d0f7f2066cd3"><code>f9495da</code></a> Version bump v0.5.2</li>
<li><a href="https://github.com/amyreese/sphinx-mdinclude/commit/c7f4a3147e9e103ad9da71f7d33ae520936be6fb"><code>c7f4a31</code></a> Make docs buildable without installing</li>
<li><a href="https://github.com/amyreese/sphinx-mdinclude/commit/80f4f69b269462799de82a5ad9ed3bee53072f07"><code>80f4f69</code></a> Upgrade deps to latest versions</li>
<li><a href="https://github.com/amyreese/sphinx-mdinclude/commit/440c9771ce30cfe5d62dc0e83242c875e1c83394"><code>440c977</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/amyreese/sphinx-mdinclude/issues/9">#9</a> from amyreese/docutils</li>
<li><a href="https://github.com/amyreese/sphinx-mdinclude/commit/3c2c1550d18351e32f11d0c5bd275f3b37667f06"><code>3c2c155</code></a> Import docutils types from new locations</li>
<li><a href="https://github.com/amyreese/sphinx-mdinclude/commit/d5a950df25469542a35d938b1a6a09dbcbc85bb5"><code>d5a950d</code></a> Update names, url, and copyright</li>
<li>See full diff in <a href="https://github.com/amyreese/sphinx-mdinclude/compare/v0.5.1...v0.5.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sphinx-mdinclude&package-manager=pip&previous-version=0.5.1&new-version=0.5.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 89d0c3a..f9dd802 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -6,7 +6,7 @@ mypy==0.971
 pessimist==0.9.3
 setuptools_scm==6.4.2
 sphinx==4.3.2
-sphinx-mdinclude==0.5.1
+sphinx-mdinclude==0.5.2
 tox==3.25.1
 twine==3.8.0
 ufmt==1.3.3
diff --git a/setup.cfg b/setup.cfg
index 2d83454..ccf78c1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -37,8 +37,8 @@ include = usort/*
 omit = usort/tests/*
 
 [coverage:report]
-fail_under = 70
-precision = 1
+fail_under = 92
+precision = 0
 show_missing = True
 skip_covered = True
 
diff --git a/usort/sorting.py b/usort/sorting.py
index 666728c..4c5f336 100644
--- a/usort/sorting.py
+++ b/usort/sorting.py
@@ -163,7 +163,14 @@ class ImportSorter:
             # move imported names metadata
             for imp in new.imports:
                 for key in list(imp.imported_names):
-                    new.imported_names[key] = block.imported_names.pop(key)
+                    name = block.imported_names.pop(key, None)
+                    if name is not None:
+                        # Normally we wouldn't see multiple copies of a key because that
+                        # would have caused a block split already, but if an import is
+                        # just verbatim repeated, it's not technically shadowing.
+                        # If that's the case, we've already popped it and achieved the
+                        # goal of this block.
+                        new.imported_names[key] = name
 
         return new
 


Test patch:
diff --git a/usort/tests/__init__.py b/usort/tests/__init__.py
index 5ca0e12..67dfb7d 100644
--- a/usort/tests/__init__.py
+++ b/usort/tests/__init__.py
@@ -6,6 +6,7 @@
 from .cli import CliTest
 from .config import ConfigTest
 from .functional import BasicOrderingTest, UsortStringFunctionalTest
+from .sorting import SplitTest
 from .stdlibs import StdlibsTest
 from .translate import IsSortableTest, SortableImportTest
 from .types import TypesTest
@@ -21,4 +22,5 @@ __all__ = [
     "StdlibsTest",
     "TypesTest",
     "UtilTest",
+    "SplitTest",
 ]
diff --git a/usort/tests/functional.py b/usort/tests/functional.py
index c486cf1..b48d1cd 100644
--- a/usort/tests/functional.py
+++ b/usort/tests/functional.py
@@ -897,6 +897,21 @@ numpy = ["numpy", "pandas"]
             """,
         )
 
+    def test_sort_merging_and_implicit_blocks(self) -> None:
+        self.assertUsortResult(
+            """
+                from a import x
+                from b import y
+                from b import y
+                from c import x
+            """,
+            """
+                from a import x
+                from b import y
+                from c import x
+            """,
+        )
+
     def test_skip_directives(self) -> None:
         """Test both usort:skip and isort:skip on single line imports"""
         self.assertUsortResult(
diff --git a/usort/tests/sorting.py b/usort/tests/sorting.py
new file mode 100644
index 0000000..446740b
--- /dev/null
+++ b/usort/tests/sorting.py
@@ -0,0 +1,30 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+import unittest
+from pathlib import Path
+
+import libcst as cst
+
+from ..config import Config
+
+from ..sorting import ImportSorter
+
+
+class SplitTest(unittest.TestCase):
+    def test_split_block(self) -> None:
+        mod = cst.parse_module(
+            b"""\
+from a import x
+from b import y
+from b import y
+from c import x
+"""
+        )
+        x = ImportSorter(module=mod, path=Path(), config=Config())
+        blocks = x.sortable_blocks(mod.children)  # type: ignore
+        self.assertEqual(2, len(blocks))
+        self.assertEqual({"x": "a.x"}, blocks[0].imported_names)
+        self.assertEqual({"y": "b.y", "x": "c.x"}, blocks[1].imported_names)