pyflyby 1.10.0__tar.gz → 1.10.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyflyby might be problematic. Click here for more details.

Files changed (140) hide show
  1. {pyflyby-1.10.0 → pyflyby-1.10.2}/.github/workflows/docs.yml +1 -1
  2. {pyflyby-1.10.0 → pyflyby-1.10.2}/.github/workflows/lint.yml +1 -2
  3. {pyflyby-1.10.0 → pyflyby-1.10.2}/.github/workflows/test.yml +1 -1
  4. {pyflyby-1.10.0 → pyflyby-1.10.2}/PKG-INFO +27 -9
  5. {pyflyby-1.10.0 → pyflyby-1.10.2}/README.rst +22 -5
  6. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/tidy-imports +8 -1
  7. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/conf.py +1 -1
  8. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_cmdline.py +62 -19
  9. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_interactive.py +2 -2
  10. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_modules.py +10 -4
  11. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_version.py +1 -1
  12. {pyflyby-1.10.0 → pyflyby-1.10.2}/meson.build +1 -1
  13. {pyflyby-1.10.0 → pyflyby-1.10.2}/pyproject.toml +4 -3
  14. {pyflyby-1.10.0 → pyflyby-1.10.2}/src/_fast_iter_modules.cpp +26 -15
  15. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_cmdline.py +261 -1
  16. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_modules.py +26 -9
  17. {pyflyby-1.10.0 → pyflyby-1.10.2}/.github/dependabot.yml +0 -0
  18. {pyflyby-1.10.0 → pyflyby-1.10.2}/.gitignore +0 -0
  19. {pyflyby-1.10.0 → pyflyby-1.10.2}/.pyflyby +0 -0
  20. {pyflyby-1.10.0 → pyflyby-1.10.2}/CONTRIBUTING.md +0 -0
  21. {pyflyby-1.10.0 → pyflyby-1.10.2}/LICENSE.txt +0 -0
  22. {pyflyby-1.10.0 → pyflyby-1.10.2}/MANIFEST.in +0 -0
  23. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/autoipython +0 -0
  24. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/autopython +0 -0
  25. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/collect-exports +0 -0
  26. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/collect-imports +0 -0
  27. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/create-imports +0 -0
  28. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/find-import +0 -0
  29. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/list-bad-xrefs +0 -0
  30. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/prune-broken-imports +0 -0
  31. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/py +0 -0
  32. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/pyflyby-diff +0 -0
  33. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/reformat-imports +0 -0
  34. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/replace-star-imports +0 -0
  35. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/saveframe +0 -0
  36. {pyflyby-1.10.0 → pyflyby-1.10.2}/bin/transform-imports +0 -0
  37. {pyflyby-1.10.0 → pyflyby-1.10.2}/codecov.yml +0 -0
  38. {pyflyby-1.10.0 → pyflyby-1.10.2}/conftest.py +0 -0
  39. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/LICENSE.txt +0 -0
  40. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/Makefile +0 -0
  41. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/TODO.txt +0 -0
  42. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/__init__.py +0 -0
  43. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/api.rst +0 -0
  44. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/autoimp.rst +0 -0
  45. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/cmdline.rst +0 -0
  46. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/comms.rst +0 -0
  47. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/dbg.rst +0 -0
  48. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/file.rst +0 -0
  49. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/flags.rst +0 -0
  50. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/format.rst +0 -0
  51. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/idents.rst +0 -0
  52. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/importclns.rst +0 -0
  53. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/importdb.rst +0 -0
  54. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/imports2s.rst +0 -0
  55. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/importstmt.rst +0 -0
  56. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/interactive.rst +0 -0
  57. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/livepatch.rst +0 -0
  58. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/log.rst +0 -0
  59. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/modules.rst +0 -0
  60. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/parse.rst +0 -0
  61. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/py.rst +0 -0
  62. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/api/util.rst +0 -0
  63. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/autoipython.rst +0 -0
  64. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/cli.rst +0 -0
  65. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/collect_exports.rst +0 -0
  66. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/collect_imports.rst +0 -0
  67. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/find_import.rst +0 -0
  68. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/prune_broken_imports.rst +0 -0
  69. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/py.rst +0 -0
  70. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/pyflyby_diff.rst +0 -0
  71. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/reformat_imports.rst +0 -0
  72. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/replace_star_imports.rst +0 -0
  73. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/tidy_imports.rst +0 -0
  74. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/cli/transform_imports.rst +0 -0
  75. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/index.rst +0 -0
  76. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/make.bat +0 -0
  77. {pyflyby-1.10.0 → pyflyby-1.10.2}/doc/testing.txt +0 -0
  78. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/.gitignore +0 -0
  79. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/canonical.py +0 -0
  80. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/common.py +0 -0
  81. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/forget.py +0 -0
  82. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/mandatory.py +0 -0
  83. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/numpy.py +0 -0
  84. {pyflyby-1.10.0 → pyflyby-1.10.2}/etc/pyflyby/std.py +0 -0
  85. {pyflyby-1.10.0 → pyflyby-1.10.2}/github_deploy_key_deshaw_pyflyby.enc +0 -0
  86. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/emacs/pyflyby.el +0 -0
  87. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/.gitignore +0 -0
  88. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/.gitignore +0 -0
  89. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/__init__.py +0 -0
  90. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/__main__.py +0 -0
  91. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_autoimp.py +0 -0
  92. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_comms.py +0 -0
  93. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_dbg.py +0 -0
  94. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_docxref.py +0 -0
  95. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_dynimp.py +0 -0
  96. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_file.py +0 -0
  97. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_flags.py +0 -0
  98. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_format.py +0 -0
  99. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_idents.py +0 -0
  100. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_import_sorting.py +0 -0
  101. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_importclns.py +0 -0
  102. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_importdb.py +0 -0
  103. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_imports2s.py +0 -0
  104. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_importstmt.py +0 -0
  105. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_livepatch.py +0 -0
  106. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_log.py +0 -0
  107. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_parse.py +0 -0
  108. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_py.py +0 -0
  109. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_saveframe.py +0 -0
  110. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_saveframe_reader.py +0 -0
  111. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/_util.py +0 -0
  112. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/autoimport.py +0 -0
  113. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/importdb.py +0 -0
  114. {pyflyby-1.10.0 → pyflyby-1.10.2}/lib/python/pyflyby/meson.build +0 -0
  115. {pyflyby-1.10.0 → pyflyby-1.10.2}/libexec/pyflyby/colordiff +0 -0
  116. {pyflyby-1.10.0 → pyflyby-1.10.2}/libexec/pyflyby/diff-colorize +0 -0
  117. {pyflyby-1.10.0 → pyflyby-1.10.2}/pytest.ini +0 -0
  118. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/__init__.py +0 -0
  119. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_0testconfig.py +0 -0
  120. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_autoimp.py +0 -0
  121. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_docxref.py +0 -0
  122. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_file.py +0 -0
  123. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_flags.py +0 -0
  124. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_format.py +0 -0
  125. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_idents.py +0 -0
  126. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_importclns.py +0 -0
  127. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_importdb.py +0 -0
  128. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_imports2s.py +0 -0
  129. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_importstmt.py +0 -0
  130. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_interactive.py +0 -0
  131. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_jupyterlab_pyflyby.py +0 -0
  132. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_livepatch.py +0 -0
  133. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_parse.py +0 -0
  134. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_py.py +0 -0
  135. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_saveframe.py +0 -0
  136. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_saveframe_reader.py +0 -0
  137. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/test_util.py +0 -0
  138. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/tests_sorts.py +0 -0
  139. {pyflyby-1.10.0 → pyflyby-1.10.2}/tests/xrefs.py +0 -0
  140. {pyflyby-1.10.0 → pyflyby-1.10.2}/tox.ini +0 -0
@@ -9,7 +9,7 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - uses: actions/checkout@v5
12
- - uses: actions/setup-python@v5
12
+ - uses: actions/setup-python@v6
13
13
  - name: Install dependencies
14
14
  run: |
15
15
  pip install -U pip
@@ -8,7 +8,7 @@ jobs:
8
8
  steps:
9
9
  - uses: actions/checkout@v5
10
10
  - name: Set up Python
11
- uses: actions/setup-python@v5
11
+ uses: actions/setup-python@v6
12
12
  with:
13
13
  python-version: 3.13
14
14
  - name: Install pyflyby
@@ -17,7 +17,6 @@ jobs:
17
17
  # https://mesonbuild.com/meson-python/how-to-guides/editable-installs.html#build-dependencies
18
18
  # for details.
19
19
  pip install meson-python meson ninja pybind11>=2.10.4
20
- pip install setuptools wheel # needed for epydoc
21
20
  pip install --no-build-isolation -ve . --group lint
22
21
  - name: Mypy
23
22
  run: |
@@ -27,7 +27,7 @@ jobs:
27
27
  steps:
28
28
  - uses: actions/checkout@v5
29
29
  - name: Set up Python ${{ matrix.python-version }}
30
- uses: actions/setup-python@v5
30
+ uses: actions/setup-python@v6
31
31
  with:
32
32
  python-version: ${{ matrix.python-version }}
33
33
  - name: Install pyflyby
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyflyby
3
- Version: 1.10.0
3
+ Version: 1.10.2
4
4
  Summary: pyflyby - Python development productivity tools, in particular automatic import management
5
5
  Author-Email: Karl Chen <quarl@8166.clguba.z.quarl.org>
6
6
  License-Expression: MIT
@@ -20,9 +20,7 @@ Requires-Dist: six
20
20
  Requires-Dist: tomli; python_version < "3.11"
21
21
  Requires-Dist: typing_extensions>=4.6; python_version < "3.12"
22
22
  Requires-Dist: prompt_toolkit
23
- Requires-Dist: epydoc
24
- Requires-Dist: wheel
25
- Requires-Dist: appdirs
23
+ Requires-Dist: platformdirs
26
24
  Provides-Extra: test
27
25
  Requires-Dist: build; extra == "test"
28
26
  Requires-Dist: coverage; extra == "test"
@@ -38,6 +36,9 @@ Requires-Dist: pytest-cov; extra == "test"
38
36
  Requires-Dist: pytest-json-report; extra == "test"
39
37
  Requires-Dist: pytest<=8; extra == "test"
40
38
  Requires-Dist: requests; extra == "test"
39
+ Requires-Dist: epydoc; extra == "test"
40
+ Requires-Dist: wheel; extra == "test"
41
+ Requires-Dist: setuptools; extra == "test"
41
42
  Description-Content-Type: text/x-rst
42
43
 
43
44
  #########
@@ -209,6 +210,7 @@ For example:
209
210
 
210
211
  Replace /tmp/foo.py? [y/N]
211
212
 
213
+ To exclude a file, use `--exclude <pattern>`.
212
214
 
213
215
  Quick start: import libraries
214
216
  =============================
@@ -480,21 +482,37 @@ Per-Project configuration of tidy-imports
480
482
  =========================================
481
483
 
482
484
  You can configure Pyflyby on a per-repository basis by using the
483
- `[tool.pyflyby]` section of `pyproject.toml` files. Pyflyby will look in current
484
- working directory and all it's parent until it find a `pyproject.toml` file from
485
+ ``[tool.pyflyby]`` section of ``pyproject.toml`` files. Pyflyby will look in current
486
+ working directory and all it's parent until it find a ``pyproject.toml`` file from
485
487
  which it will load the defaults.
486
488
 
487
489
 
488
490
  Most of the long command line flags default values can be configured in this
489
- section. Simply use the long form option name by replacing dashes `-` by
490
- underscore `_`. For long option that have the form `--xxx` and `--no-xxx`, you
491
- can assign a boolean to `xxx`. For example::
491
+ section. Simply use the long form option name by replacing dashes ``-`` by
492
+ underscore ``_``. For long option that have the form ``--xxx`` and ``--no-xxx``, you
493
+ can assign a boolean to ``xxx``. For example::
494
+
495
+ .. code:: toml
492
496
 
493
497
  [tool.pyflyby]
494
498
  add_missing=true
495
499
  from_spaces=7
496
500
  remove_unused=false
497
501
 
502
+ To exclude files from ``tidy-imports``, add an exclusion pattern to
503
+ ``tool.pyflyby.tidy-imports.exclude``:
504
+
505
+ .. code:: toml
506
+
507
+ [tool.pyflyby.tidy-imports]
508
+ exclude = [
509
+ "foo.py",
510
+ "baz/*.py"
511
+ ]
512
+
513
+ Exclusions are assumed to be relative to the project root if a ``pyproject.toml`` exists, unless an
514
+ absolute path is specified. Consult the documentation for ``pathlib.Path.match`` for information about
515
+ valid exclusion patterns.
498
516
 
499
517
  Emacs support
500
518
  =============
@@ -167,6 +167,7 @@ For example:
167
167
 
168
168
  Replace /tmp/foo.py? [y/N]
169
169
 
170
+ To exclude a file, use `--exclude <pattern>`.
170
171
 
171
172
  Quick start: import libraries
172
173
  =============================
@@ -438,21 +439,37 @@ Per-Project configuration of tidy-imports
438
439
  =========================================
439
440
 
440
441
  You can configure Pyflyby on a per-repository basis by using the
441
- `[tool.pyflyby]` section of `pyproject.toml` files. Pyflyby will look in current
442
- working directory and all it's parent until it find a `pyproject.toml` file from
442
+ ``[tool.pyflyby]`` section of ``pyproject.toml`` files. Pyflyby will look in current
443
+ working directory and all it's parent until it find a ``pyproject.toml`` file from
443
444
  which it will load the defaults.
444
445
 
445
446
 
446
447
  Most of the long command line flags default values can be configured in this
447
- section. Simply use the long form option name by replacing dashes `-` by
448
- underscore `_`. For long option that have the form `--xxx` and `--no-xxx`, you
449
- can assign a boolean to `xxx`. For example::
448
+ section. Simply use the long form option name by replacing dashes ``-`` by
449
+ underscore ``_``. For long option that have the form ``--xxx`` and ``--no-xxx``, you
450
+ can assign a boolean to ``xxx``. For example::
451
+
452
+ .. code:: toml
450
453
 
451
454
  [tool.pyflyby]
452
455
  add_missing=true
453
456
  from_spaces=7
454
457
  remove_unused=false
455
458
 
459
+ To exclude files from ``tidy-imports``, add an exclusion pattern to
460
+ ``tool.pyflyby.tidy-imports.exclude``:
461
+
462
+ .. code:: toml
463
+
464
+ [tool.pyflyby.tidy-imports]
465
+ exclude = [
466
+ "foo.py",
467
+ "baz/*.py"
468
+ ]
469
+
470
+ Exclusions are assumed to be relative to the project root if a ``pyproject.toml`` exists, unless an
471
+ absolute path is specified. Consult the documentation for ``pathlib.Path.match`` for information about
472
+ valid exclusion patterns.
456
473
 
457
474
  Emacs support
458
475
  =============
@@ -85,6 +85,8 @@ def _addopts(parser):
85
85
  default=True, action='store_false',
86
86
  help=hfmt('''
87
87
  Don't canonicalize imports.'''))
88
+ parser.add_option('--exclude', type='string', dest='exclude',
89
+ action='append', help=hfmt('Files to exclude from formatting.'))
88
90
 
89
91
 
90
92
  def transform_callback(option, opt_str, value, group):
@@ -156,7 +158,12 @@ def main() -> None:
156
158
  cannonical_imports = sorted_imports
157
159
  return cannonical_imports
158
160
 
159
- process_actions(args, options.actions, modify)
161
+ cmdline_exclude = getattr(options, "exclude")
162
+ process_actions(
163
+ args,
164
+ options.actions, modify,
165
+ exclude=default_config.get('tidy-imports', {}).get('exclude', []) + (cmdline_exclude if cmdline_exclude else [])
166
+ )
160
167
 
161
168
 
162
169
  if __name__ == '__main__':
@@ -45,7 +45,7 @@ autodoc_default_options = {
45
45
 
46
46
  autodoc_mock_imports = [
47
47
  "pyflyby._fast_iter_modules",
48
- "appdirs",
48
+ "platformdirs",
49
49
  "prompt_toolkit",
50
50
  ]
51
51
 
@@ -27,6 +27,10 @@ else:
27
27
  from tomllib import loads
28
28
 
29
29
 
30
+ class ConfigurationError(Exception):
31
+ """Exception class indicating a configuration error."""
32
+
33
+
30
34
  def hfmt(s):
31
35
  return dedent(s).strip()
32
36
 
@@ -232,26 +236,17 @@ def parse_args(addopts=None, import_format_params=False, modify_action_params=Fa
232
236
  only if it is necessary to prevent exceeding the
233
237
  width (by default 79).
234
238
  '''))
235
- def uniform_callback(option, opt_str, value, parser):
236
- parser.values.separate_from_imports = False
237
- parser.values.from_spaces = 3
238
- parser.values.align_imports = '32'
239
- group.add_option('--uniform', '-u', action="callback",
240
- callback=uniform_callback,
239
+ group.add_option('--uniform', '-u', action="store_true",
241
240
  help=hfmt('''
242
241
  (Default) Shortcut for --no-separate-from-imports
243
242
  --from-spaces=3 --align-imports=32.'''))
244
- def unaligned_callback(option, opt_str, value, parser):
245
- parser.values.separate_from_imports = True
246
- parser.values.from_spaces = 1
247
- parser.values.align_imports = '0'
248
- group.add_option('--unaligned', '-n', action="callback",
249
- callback=unaligned_callback,
243
+ group.add_option('--unaligned', '-n', action="store_true",
250
244
  help=hfmt('''
251
245
  Shortcut for --separate-from-imports
252
246
  --from-spaces=1 --align-imports=0.'''))
253
247
 
254
248
  parser.add_option_group(group)
249
+
255
250
  if addopts is not None:
256
251
  addopts(parser)
257
252
  # This is the only way to provide a default value for an option with a
@@ -260,7 +255,22 @@ def parse_args(addopts=None, import_format_params=False, modify_action_params=Fa
260
255
  args = ["--symlinks=error"] + sys.argv[1:]
261
256
  else:
262
257
  args = None
258
+
263
259
  options, args = parser.parse_args(args=args)
260
+
261
+ # Set these manually rather than in a callback option because callback
262
+ # options don't get triggered by OptionParser.set_default (which is
263
+ # used when setting values via pyproject.toml)
264
+ if getattr(options, "unaligned", False):
265
+ parser.values.separate_from_imports = True
266
+ parser.values.from_spaces = 1
267
+ parser.values.align_imports = '0'
268
+
269
+ if getattr(options, "uniform", False):
270
+ parser.values.separate_from_imports = False
271
+ parser.values.from_spaces = 3
272
+ parser.values.align_imports = '32'
273
+
264
274
  if import_format_params:
265
275
  align_imports_args = [int(x.strip())
266
276
  for x in options.align_imports.split(",")]
@@ -380,14 +390,35 @@ class Modifier(object):
380
390
 
381
391
 
382
392
  def process_actions(filenames:List[str], actions, modify_function,
383
- reraise_exceptions=()):
393
+ reraise_exceptions=(), exclude=()):
394
+
395
+ if not isinstance(exclude, (list, tuple)):
396
+ raise ConfigurationError(
397
+ "Exclusions must be a list of filenames/patterns to exclude."
398
+ )
399
+
384
400
  errors = []
385
401
  def on_error_filename_arg(arg):
386
402
  print("%s: bad filename %s" % (sys.argv[0], arg), file=sys.stderr)
387
403
  errors.append("%s: bad filename" % (arg,))
388
- filenames = filename_args(filenames, on_error=on_error_filename_arg)
404
+ filename_objs = filename_args(filenames, on_error=on_error_filename_arg)
389
405
  exit_code = 0
390
- for filename in filenames:
406
+ for filename in filename_objs:
407
+
408
+ # Log any matching exclusion patterns before ignoring, if applicable
409
+ matching_excludes = []
410
+ for pattern in exclude:
411
+ if Path(str(filename)).match(str(pattern)):
412
+ matching_excludes.append(pattern)
413
+ if any(matching_excludes):
414
+ msg = f"{filename} matches exclusion pattern"
415
+ if len(matching_excludes) == 1:
416
+ msg += f": {matching_excludes[0]}"
417
+ else:
418
+ msg += f"s: {matching_excludes}"
419
+ logger.info(msg)
420
+ continue
421
+
391
422
  try:
392
423
  m = Modifier(modify_function, filename)
393
424
  for action in actions:
@@ -533,16 +564,28 @@ symlink_callbacks = {
533
564
  'replace': symlink_replace,
534
565
  }
535
566
 
536
- def _get_pyproj_toml_config():
537
- """
538
- Try to find current project pyproject.toml
567
+ def _get_pyproj_toml_file():
568
+ """Try to find the location of the current project pyproject.toml
539
569
  in cwd or parents directories.
570
+
571
+ If no pyproject.toml can be found, None is returned.
540
572
  """
541
573
  cwd = Path(os.getcwd())
542
574
 
543
575
  for pth in [cwd] + list(cwd.parents):
544
576
  pyproj_toml = pth /'pyproject.toml'
545
577
  if pyproj_toml.exists() and pyproj_toml.is_file():
546
- return loads(pyproj_toml.read_text())
578
+ return pyproj_toml
547
579
 
548
580
  return None
581
+
582
+ def _get_pyproj_toml_config():
583
+ """Return the toml contents of the current pyproject.toml.
584
+
585
+ If no pyproject.toml can be found in cwd or parent directories,
586
+ None is returned.
587
+ """
588
+ pyproject_toml = _get_pyproj_toml_file()
589
+ if pyproject_toml is not None:
590
+ return loads(pyproject_toml.read_text())
591
+ return None
@@ -1724,14 +1724,14 @@ class AutoImporter:
1724
1724
  return __original__.fget(completer) + [completer.python_matcher]
1725
1725
 
1726
1726
  @self._advise(completer.global_matches)
1727
- def global_matches_with_autoimport(name):
1727
+ def global_matches_with_autoimport(name, *args, **kwargs):
1728
1728
  old_global_namespace = completer.global_namespace
1729
1729
  completer.global_namespace = NamespaceWithPotentialImports(
1730
1730
  old_global_namespace,
1731
1731
  ip=self._ip
1732
1732
  )
1733
1733
  try:
1734
- return self._safe_call(__original__, name)
1734
+ return self._safe_call(__original__, name, *args, **kwargs)
1735
1735
  finally:
1736
1736
  completer.global_namespace = old_global_namespace
1737
1737
 
@@ -4,7 +4,6 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import appdirs
8
7
  import ast
9
8
  from functools import cached_property, total_ordering
10
9
  import hashlib
@@ -14,6 +13,7 @@ import json
14
13
  import os
15
14
  import pathlib
16
15
  import pkgutil
16
+ import platformdirs
17
17
  import textwrap
18
18
 
19
19
  from pyflyby._fast_iter_modules \
@@ -45,7 +45,7 @@ def rebuild_import_cache():
45
45
  The cache is deleted before calling _fast_iter_modules, which repopulates the cache.
46
46
  """
47
47
  for path in pathlib.Path(
48
- appdirs.user_cache_dir(appname='pyflyby', appauthor=False)
48
+ platformdirs.user_cache_dir(appname='pyflyby', appauthor=False)
49
49
  ).iterdir():
50
50
  _remove_import_cache_dir(path)
51
51
  _fast_iter_modules()
@@ -595,8 +595,14 @@ def _cached_module_finder(
595
595
  Tuples containing (prefix+module name, a bool indicating whether the module is a
596
596
  package or not)
597
597
  """
598
+ if os.environ.get("PYFLYBY_DISABLE_CACHE", "0") == "1":
599
+ modules = _iter_file_finder_modules(importer, SUFFIXES)
600
+ for module, ispkg in modules:
601
+ yield prefix + module, ispkg
602
+ return
603
+
598
604
  cache_dir = pathlib.Path(
599
- appdirs.user_cache_dir(appname='pyflyby', appauthor=False)
605
+ platformdirs.user_cache_dir(appname='pyflyby', appauthor=False)
600
606
  ) / hashlib.sha256(str(importer.path).encode()).hexdigest()
601
607
  cache_file = cache_dir / str(os.stat(importer.path).st_mtime_ns)
602
608
 
@@ -610,7 +616,7 @@ def _cached_module_finder(
610
616
  for path in cache_dir.iterdir():
611
617
  _remove_import_cache_dir(path)
612
618
 
613
- if os.environ.get("PYFLYBY_SUPPRESS_CACHE_REBUILD_LOGS", 0) != "1":
619
+ if os.environ.get("PYFLYBY_SUPPRESS_CACHE_REBUILD_LOGS", "1") != "1":
614
620
  logger.info(f"Rebuilding cache for {_format_path(importer.path)}...")
615
621
 
616
622
  modules = _iter_file_finder_modules(importer, SUFFIXES)
@@ -4,4 +4,4 @@
4
4
  # http://creativecommons.org/publicdomain/zero/1.0/
5
5
 
6
6
 
7
- __version__ = "1.10.0"
7
+ __version__ = "1.10.2"
@@ -1,7 +1,7 @@
1
1
  project(
2
2
  'pyflyby',
3
3
  'cpp',
4
- version: '1.10.0',
4
+ version: '1.10.2',
5
5
  default_options: 'cpp_std=c++17'
6
6
  )
7
7
 
@@ -30,9 +30,7 @@ dependencies = [
30
30
  "tomli; python_version<'3.11'",
31
31
  "typing_extensions>=4.6; python_version<'3.12'",
32
32
  'prompt_toolkit',
33
- 'epydoc',
34
- 'wheel', # required by epydoc, but not listed as a dependency
35
- 'appdirs',
33
+ 'platformdirs',
36
34
  ]
37
35
  [project.urls]
38
36
  Homepage = "https://pypi.org/project/pyflyby/"
@@ -59,6 +57,9 @@ test = [
59
57
  'pytest-json-report',
60
58
  'pytest<=8',
61
59
  'requests',
60
+ 'epydoc',
61
+ 'wheel', # required by epydoc, but not listed as a dependency
62
+ 'setuptools', # required by epydoc, but not listed as a dependency
62
63
  ]
63
64
 
64
65
  [dependency-groups]
@@ -62,24 +62,35 @@ _iter_file_finder_modules(
62
62
  return ret;
63
63
  }
64
64
 
65
- for (auto const &entry : fs::directory_iterator(path)) {
66
- fs::path entry_path = entry.path();
67
- fs::path filename = entry_path.filename();
68
- std::string modname = getmodulename(filename, suffixes);
65
+ // Attempt to iterate the directory. If the directory is unreadable for any reason
66
+ // (e.g., permissions, non-existent, or other system errors), fs::directory_iterator
67
+ // will throw a filesystem_error. We catch this and return an empty list for this path.
68
+ try {
69
+ for (auto const &entry : fs::directory_iterator(path)) {
70
+ fs::path entry_path = entry.path();
71
+ fs::path filename = entry_path.filename();
72
+ std::string modname = getmodulename(filename, suffixes);
69
73
 
70
74
 
71
- if (modname == "" && fs::is_directory(entry_path) &&
72
- filename.string().find(".") == std::string::npos &&
73
- fs::is_regular_file(entry_path / "__init__.py") // Is this a package?
74
- ) {
75
- ret.push_back(std::make_tuple(filename.string(), true));
76
- } else if (modname == "__init__") {
77
- continue;
78
- } else if (modname != "" && modname.find(".") == std::string::npos) {
79
- ret.push_back(std::make_tuple(modname,
80
- false // This is definitely not a package
81
- ));
75
+ if (modname == "" && fs::is_directory(entry_path) &&
76
+ filename.string().find(".") == std::string::npos &&
77
+ fs::is_regular_file(entry_path / "__init__.py") // Is this a package?
78
+ ) {
79
+ ret.push_back(std::make_tuple(filename.string(), true));
80
+ } else if (modname == "__init__") {
81
+ continue;
82
+ } else if (modname != "" && modname.find(".") == std::string::npos) {
83
+ ret.push_back(std::make_tuple(modname,
84
+ false // This is definitely not a package
85
+ ));
86
+ }
82
87
  }
88
+ } catch (const fs::filesystem_error& e) {
89
+ // If an error occurs during directory iteration (e.g., permissions denied,
90
+ // directory removed concurrently), we treat it as unreadable/inaccessible
91
+ // and return the current (potentially empty) list, effectively skipping this path.
92
+ // We could log the error 'e.what()' here if desired for debugging.
93
+ return ret;
83
94
  }
84
95
 
85
96
  return ret;