pyflyby 1.9.11__tar.gz → 1.9.12__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 (161) hide show
  1. {pyflyby-1.9.11/lib/python/pyflyby.egg-info → pyflyby-1.9.12}/PKG-INFO +1 -1
  2. {pyflyby-1.9.11/lib/python → pyflyby-1.9.12/bin}/pyflyby/_py.py +77 -0
  3. {pyflyby-1.9.11/lib/python → pyflyby-1.9.12/bin}/pyflyby/_version.py +1 -1
  4. {pyflyby-1.9.11/bin → pyflyby-1.9.12/lib/python}/pyflyby/_py.py +77 -0
  5. {pyflyby-1.9.11/bin → pyflyby-1.9.12/lib/python}/pyflyby/_version.py +1 -1
  6. {pyflyby-1.9.11 → pyflyby-1.9.12/lib/python/pyflyby.egg-info}/PKG-INFO +1 -1
  7. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_py.py +61 -0
  8. {pyflyby-1.9.11 → pyflyby-1.9.12}/.pyflyby +0 -0
  9. {pyflyby-1.9.11 → pyflyby-1.9.12}/LICENSE.txt +0 -0
  10. {pyflyby-1.9.11 → pyflyby-1.9.12}/MANIFEST.in +0 -0
  11. {pyflyby-1.9.11 → pyflyby-1.9.12}/README.rst +0 -0
  12. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/autoipython +0 -0
  13. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/autopython +0 -0
  14. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/collect-exports +0 -0
  15. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/collect-imports +0 -0
  16. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/create-imports +0 -0
  17. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/find-import +0 -0
  18. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/list-bad-xrefs +0 -0
  19. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/prune-broken-imports +0 -0
  20. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/py +0 -0
  21. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/__init__.py +0 -0
  22. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/__main__.py +0 -0
  23. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_autoimp.py +0 -0
  24. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_cmdline.py +0 -0
  25. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_comms.py +0 -0
  26. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_dbg.py +0 -0
  27. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_docxref.py +0 -0
  28. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_dynimp.py +0 -0
  29. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_file.py +0 -0
  30. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_flags.py +0 -0
  31. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_format.py +0 -0
  32. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_idents.py +0 -0
  33. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_import_sorting.py +0 -0
  34. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_importclns.py +0 -0
  35. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_importdb.py +0 -0
  36. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_imports2s.py +0 -0
  37. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_importstmt.py +0 -0
  38. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_interactive.py +0 -0
  39. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_livepatch.py +0 -0
  40. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_log.py +0 -0
  41. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_modules.py +0 -0
  42. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_parse.py +0 -0
  43. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_saveframe.py +0 -0
  44. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_saveframe_reader.py +0 -0
  45. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/_util.py +0 -0
  46. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/autoimport.py +0 -0
  47. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby/importdb.py +0 -0
  48. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/pyflyby-diff +0 -0
  49. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/reformat-imports +0 -0
  50. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/replace-star-imports +0 -0
  51. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/saveframe +0 -0
  52. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/tidy-imports +0 -0
  53. {pyflyby-1.9.11 → pyflyby-1.9.12}/bin/transform-imports +0 -0
  54. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/LICENSE.txt +0 -0
  55. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/Makefile +0 -0
  56. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/TODO.txt +0 -0
  57. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/__init__.py +0 -0
  58. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/api.rst +0 -0
  59. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/autoimp.rst +0 -0
  60. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/cmdline.rst +0 -0
  61. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/comms.rst +0 -0
  62. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/dbg.rst +0 -0
  63. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/file.rst +0 -0
  64. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/flags.rst +0 -0
  65. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/format.rst +0 -0
  66. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/idents.rst +0 -0
  67. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/importclns.rst +0 -0
  68. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/importdb.rst +0 -0
  69. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/imports2s.rst +0 -0
  70. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/importstmt.rst +0 -0
  71. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/interactive.rst +0 -0
  72. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/livepatch.rst +0 -0
  73. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/log.rst +0 -0
  74. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/modules.rst +0 -0
  75. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/parse.rst +0 -0
  76. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/py.rst +0 -0
  77. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/api/util.rst +0 -0
  78. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/autoipython.rst +0 -0
  79. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/cli.rst +0 -0
  80. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/collect_exports.rst +0 -0
  81. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/collect_imports.rst +0 -0
  82. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/find_import.rst +0 -0
  83. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/prune_broken_imports.rst +0 -0
  84. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/py.rst +0 -0
  85. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/pyflyby_diff.rst +0 -0
  86. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/reformat_imports.rst +0 -0
  87. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/replace_star_imports.rst +0 -0
  88. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/tidy_imports.rst +0 -0
  89. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/cli/transform_imports.rst +0 -0
  90. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/conf.py +0 -0
  91. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/index.rst +0 -0
  92. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/make.bat +0 -0
  93. {pyflyby-1.9.11 → pyflyby-1.9.12}/doc/testing.txt +0 -0
  94. {pyflyby-1.9.11 → pyflyby-1.9.12}/etc/pyflyby/canonical.py +0 -0
  95. {pyflyby-1.9.11 → pyflyby-1.9.12}/etc/pyflyby/common.py +0 -0
  96. {pyflyby-1.9.11 → pyflyby-1.9.12}/etc/pyflyby/forget.py +0 -0
  97. {pyflyby-1.9.11 → pyflyby-1.9.12}/etc/pyflyby/mandatory.py +0 -0
  98. {pyflyby-1.9.11 → pyflyby-1.9.12}/etc/pyflyby/numpy.py +0 -0
  99. {pyflyby-1.9.11 → pyflyby-1.9.12}/etc/pyflyby/std.py +0 -0
  100. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/emacs/pyflyby.el +0 -0
  101. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/__init__.py +0 -0
  102. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/__main__.py +0 -0
  103. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_autoimp.py +0 -0
  104. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_cmdline.py +0 -0
  105. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_comms.py +0 -0
  106. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_dbg.py +0 -0
  107. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_docxref.py +0 -0
  108. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_dynimp.py +0 -0
  109. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_file.py +0 -0
  110. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_flags.py +0 -0
  111. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_format.py +0 -0
  112. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_idents.py +0 -0
  113. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_import_sorting.py +0 -0
  114. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_importclns.py +0 -0
  115. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_importdb.py +0 -0
  116. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_imports2s.py +0 -0
  117. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_importstmt.py +0 -0
  118. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_interactive.py +0 -0
  119. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_livepatch.py +0 -0
  120. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_log.py +0 -0
  121. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_modules.py +0 -0
  122. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_parse.py +0 -0
  123. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_saveframe.py +0 -0
  124. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_saveframe_reader.py +0 -0
  125. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/_util.py +0 -0
  126. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/autoimport.py +0 -0
  127. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby/importdb.py +0 -0
  128. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby.egg-info/SOURCES.txt +0 -0
  129. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
  130. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
  131. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby.egg-info/requires.txt +0 -0
  132. {pyflyby-1.9.11 → pyflyby-1.9.12}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
  133. {pyflyby-1.9.11 → pyflyby-1.9.12}/libexec/pyflyby/colordiff +0 -0
  134. {pyflyby-1.9.11 → pyflyby-1.9.12}/libexec/pyflyby/diff-colorize +0 -0
  135. {pyflyby-1.9.11 → pyflyby-1.9.12}/pyproject.toml +0 -0
  136. {pyflyby-1.9.11 → pyflyby-1.9.12}/setup.cfg +0 -0
  137. {pyflyby-1.9.11 → pyflyby-1.9.12}/setup.py +0 -0
  138. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/__init__.py +0 -0
  139. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_0testconfig.py +0 -0
  140. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_autoimp.py +0 -0
  141. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_cmdline.py +0 -0
  142. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_docxref.py +0 -0
  143. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_file.py +0 -0
  144. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_flags.py +0 -0
  145. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_format.py +0 -0
  146. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_idents.py +0 -0
  147. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_importclns.py +0 -0
  148. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_importdb.py +0 -0
  149. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_imports2s.py +0 -0
  150. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_importstmt.py +0 -0
  151. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_interactive.py +0 -0
  152. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_jupyterlab_pyflyby.py +0 -0
  153. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_livepatch.py +0 -0
  154. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_modules.py +0 -0
  155. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_parse.py +0 -0
  156. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_saveframe.py +0 -0
  157. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_saveframe_reader.py +0 -0
  158. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/test_util.py +0 -0
  159. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/tests_sorts.py +0 -0
  160. {pyflyby-1.9.11 → pyflyby-1.9.12}/tests/xrefs.py +0 -0
  161. {pyflyby-1.9.11 → pyflyby-1.9.12}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflyby
3
- Version: 1.9.11
3
+ Version: 1.9.12
4
4
  Summary: pyflyby - Python development productivity tools, in particular automatic import management
5
5
  Home-page: https://pypi.org/project/pyflyby/
6
6
  Author: Karl Chen
@@ -281,7 +281,9 @@ Examples
281
281
 
282
282
 
283
283
  from functools import total_ordering
284
+ from pathlib import Path
284
285
  from typing import Any
286
+ import warnings
285
287
 
286
288
  from pyflyby._util import cmp
287
289
  from shlex import quote as shquote
@@ -1410,9 +1412,84 @@ def print_result(result, output_mode):
1410
1412
  raise AssertionError("unexpected output_mode=%r" % (output_mode,))
1411
1413
 
1412
1414
 
1415
+ def _get_path_links(p: Path):
1416
+ """Gets path links including all symlinks.
1417
+
1418
+ Adapted from `IPython.core.interactiveshell.InteractiveShell.get_path_links`.
1419
+ """
1420
+ paths = [p]
1421
+ while p.is_symlink():
1422
+ new_path = Path(os.readlink(p))
1423
+ if not new_path.is_absolute():
1424
+ new_path = p.parent / new_path
1425
+ p = new_path
1426
+ paths.append(p)
1427
+ return paths
1428
+
1429
+
1430
+ def _init_virtualenv():
1431
+ """Add the current virtualenv to sys.path so the user can import modules from it.
1432
+
1433
+ A warning will appear suggesting the user installs IPython in the
1434
+ virtualenv, but for many cases, it probably works well enough.
1435
+
1436
+ Adapted `IPython.core.interactiveshell.InteractiveShell.init_virtualenv`.
1437
+ """
1438
+ if 'VIRTUAL_ENV' not in os.environ:
1439
+ # Not in a virtualenv
1440
+ return
1441
+ elif os.environ["VIRTUAL_ENV"] == "":
1442
+ warnings.warn("Virtual env path set to '', please check if this is intended.")
1443
+ return
1444
+
1445
+ p = Path(sys.executable)
1446
+ p_venv = Path(os.environ["VIRTUAL_ENV"]).resolve()
1447
+
1448
+ # fallback venv detection:
1449
+ # stdlib venv may symlink sys.executable, so we can't use realpath.
1450
+ # but others can symlink *to* the venv Python, so we can't just use sys.executable.
1451
+ # So we just check every item in the symlink tree (generally <= 3)
1452
+ paths = _get_path_links(p)
1453
+
1454
+ # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible
1455
+ if len(p_venv.parts) > 2 and p_venv.parts[1] == "cygdrive":
1456
+ drive_name = p_venv.parts[2]
1457
+ p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:])
1458
+
1459
+ if any(p_venv == p.parents[1].resolve() for p in paths):
1460
+ # Our exe is inside or has access to the virtualenv, don't need to do anything.
1461
+ return
1462
+
1463
+ if sys.platform == "win32":
1464
+ virtual_env = str(Path(os.environ["VIRTUAL_ENV"], "Lib", "site-packages"))
1465
+ else:
1466
+ virtual_env_path = Path(
1467
+ os.environ["VIRTUAL_ENV"], "lib", "python{}.{}", "site-packages"
1468
+ )
1469
+ p_ver = sys.version_info[:2]
1470
+
1471
+ # Predict version from py[thon]-x.x in the $VIRTUAL_ENV
1472
+ re_m = re.search(r"\bpy(?:thon)?([23])\.(\d+)\b", os.environ["VIRTUAL_ENV"])
1473
+ if re_m:
1474
+ predicted_path = Path(str(virtual_env_path).format(*re_m.groups()))
1475
+ if predicted_path.exists():
1476
+ p_ver = re_m.groups()
1477
+
1478
+ virtual_env = str(virtual_env_path).format(*p_ver)
1479
+
1480
+ warnings.warn(
1481
+ "Attempting to work in a virtualenv. If you encounter problems, "
1482
+ "please install pyflyby inside the virtualenv."
1483
+ )
1484
+ import site
1485
+ sys.path.insert(0, virtual_env)
1486
+ site.addsitedir(virtual_env)
1487
+
1488
+
1413
1489
  class _Namespace(object):
1414
1490
 
1415
1491
  def __init__(self):
1492
+ _init_virtualenv()
1416
1493
  self.globals = {"__name__": "__main__",
1417
1494
  "__builtin__": builtins,
1418
1495
  "__builtins__": builtins}
@@ -4,4 +4,4 @@
4
4
  # http://creativecommons.org/publicdomain/zero/1.0/
5
5
 
6
6
 
7
- __version__ = "1.9.11"
7
+ __version__ = "1.9.12"
@@ -281,7 +281,9 @@ Examples
281
281
 
282
282
 
283
283
  from functools import total_ordering
284
+ from pathlib import Path
284
285
  from typing import Any
286
+ import warnings
285
287
 
286
288
  from pyflyby._util import cmp
287
289
  from shlex import quote as shquote
@@ -1410,9 +1412,84 @@ def print_result(result, output_mode):
1410
1412
  raise AssertionError("unexpected output_mode=%r" % (output_mode,))
1411
1413
 
1412
1414
 
1415
+ def _get_path_links(p: Path):
1416
+ """Gets path links including all symlinks.
1417
+
1418
+ Adapted from `IPython.core.interactiveshell.InteractiveShell.get_path_links`.
1419
+ """
1420
+ paths = [p]
1421
+ while p.is_symlink():
1422
+ new_path = Path(os.readlink(p))
1423
+ if not new_path.is_absolute():
1424
+ new_path = p.parent / new_path
1425
+ p = new_path
1426
+ paths.append(p)
1427
+ return paths
1428
+
1429
+
1430
+ def _init_virtualenv():
1431
+ """Add the current virtualenv to sys.path so the user can import modules from it.
1432
+
1433
+ A warning will appear suggesting the user installs IPython in the
1434
+ virtualenv, but for many cases, it probably works well enough.
1435
+
1436
+ Adapted `IPython.core.interactiveshell.InteractiveShell.init_virtualenv`.
1437
+ """
1438
+ if 'VIRTUAL_ENV' not in os.environ:
1439
+ # Not in a virtualenv
1440
+ return
1441
+ elif os.environ["VIRTUAL_ENV"] == "":
1442
+ warnings.warn("Virtual env path set to '', please check if this is intended.")
1443
+ return
1444
+
1445
+ p = Path(sys.executable)
1446
+ p_venv = Path(os.environ["VIRTUAL_ENV"]).resolve()
1447
+
1448
+ # fallback venv detection:
1449
+ # stdlib venv may symlink sys.executable, so we can't use realpath.
1450
+ # but others can symlink *to* the venv Python, so we can't just use sys.executable.
1451
+ # So we just check every item in the symlink tree (generally <= 3)
1452
+ paths = _get_path_links(p)
1453
+
1454
+ # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible
1455
+ if len(p_venv.parts) > 2 and p_venv.parts[1] == "cygdrive":
1456
+ drive_name = p_venv.parts[2]
1457
+ p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:])
1458
+
1459
+ if any(p_venv == p.parents[1].resolve() for p in paths):
1460
+ # Our exe is inside or has access to the virtualenv, don't need to do anything.
1461
+ return
1462
+
1463
+ if sys.platform == "win32":
1464
+ virtual_env = str(Path(os.environ["VIRTUAL_ENV"], "Lib", "site-packages"))
1465
+ else:
1466
+ virtual_env_path = Path(
1467
+ os.environ["VIRTUAL_ENV"], "lib", "python{}.{}", "site-packages"
1468
+ )
1469
+ p_ver = sys.version_info[:2]
1470
+
1471
+ # Predict version from py[thon]-x.x in the $VIRTUAL_ENV
1472
+ re_m = re.search(r"\bpy(?:thon)?([23])\.(\d+)\b", os.environ["VIRTUAL_ENV"])
1473
+ if re_m:
1474
+ predicted_path = Path(str(virtual_env_path).format(*re_m.groups()))
1475
+ if predicted_path.exists():
1476
+ p_ver = re_m.groups()
1477
+
1478
+ virtual_env = str(virtual_env_path).format(*p_ver)
1479
+
1480
+ warnings.warn(
1481
+ "Attempting to work in a virtualenv. If you encounter problems, "
1482
+ "please install pyflyby inside the virtualenv."
1483
+ )
1484
+ import site
1485
+ sys.path.insert(0, virtual_env)
1486
+ site.addsitedir(virtual_env)
1487
+
1488
+
1413
1489
  class _Namespace(object):
1414
1490
 
1415
1491
  def __init__(self):
1492
+ _init_virtualenv()
1416
1493
  self.globals = {"__name__": "__main__",
1417
1494
  "__builtin__": builtins,
1418
1495
  "__builtins__": builtins}
@@ -4,4 +4,4 @@
4
4
  # http://creativecommons.org/publicdomain/zero/1.0/
5
5
 
6
6
 
7
- __version__ = "1.9.11"
7
+ __version__ = "1.9.12"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflyby
3
- Version: 1.9.11
3
+ Version: 1.9.12
4
4
  Summary: pyflyby - Python development productivity tools, in particular automatic import management
5
5
  Home-page: https://pypi.org/project/pyflyby/
6
6
  Author: Karl Chen
@@ -5,6 +5,7 @@
5
5
 
6
6
 
7
7
 
8
+ import ast
8
9
  import os
9
10
  import pytest
10
11
  from shutil import rmtree
@@ -13,6 +14,7 @@ import sys
13
14
  import tempfile
14
15
  from tempfile import NamedTemporaryFile, mkdtemp
15
16
  from textwrap import dedent
17
+ import venv
16
18
 
17
19
  from pyflyby._file import Filename
18
20
  from pyflyby._util import cached_attribute
@@ -2734,6 +2736,65 @@ def test_apply_not_a_function():
2734
2736
  assert "NotAFunctionError: ('Not a function', 75650517)" in result
2735
2737
 
2736
2738
 
2739
+ def test_virtualenv_recognized(tmpdir, monkeypatch):
2740
+ """Verify that virtualenv sys.path is set correctly, and that warnings are emitted."""
2741
+ if os.environ.get("VIRTUAL_ENV") is not None:
2742
+ old_path = os.environ["PATH"].split(os.pathsep)
2743
+ new_path = os.pathsep.join(old_path[1:])
2744
+
2745
+ monkeypatch.delenv("VIRTUAL_ENV")
2746
+ monkeypatch.setenv("PATH", new_path)
2747
+
2748
+ no_venv_stdout = py('print(sys.path)')
2749
+ no_venv_sys_path = ast.literal_eval(no_venv_stdout.split('\n')[-1])
2750
+
2751
+ env_dir = os.path.join(tmpdir, "venv")
2752
+ env_bin = os.path.join(env_dir, "Scripts" if os.name == "nt" else "bin")
2753
+ venv.create(env_dir)
2754
+
2755
+ # Simulate activation
2756
+ monkeypatch.setenv("VIRTUAL_ENV", env_dir)
2757
+ monkeypatch.setenv("PATH", env_bin + os.pathsep + os.environ["PATH"])
2758
+
2759
+ venv_stdout = py('print(sys.path)')
2760
+ venv_sys_path = ast.literal_eval(venv_stdout.split('\n')[-1])
2761
+
2762
+ # Check that the appropriate warning is in place when using the venv,
2763
+ # and missing if not.
2764
+ warning = (
2765
+ "UserWarning: Attempting to work in a virtualenv. "
2766
+ "If you encounter problems, please install pyflyby inside the virtualenv."
2767
+ )
2768
+ assert warning not in no_venv_stdout
2769
+ assert warning in venv_stdout
2770
+
2771
+ # Check that sys.path printed from the subprocess contains the same
2772
+ # paths as what we have in the test process
2773
+ for path in sys.path:
2774
+
2775
+ # If a path is missing from one, it must be missing from the other
2776
+ # (because both are called in subprocesses, which means that e.g.
2777
+ # the pyenv bin path won't be included in the subprocess call but
2778
+ # will be in the pytest call that runs this test)
2779
+ if path not in no_venv_stdout:
2780
+ assert path not in venv_stdout
2781
+ else:
2782
+ assert path in venv_stdout
2783
+ assert path in no_venv_stdout
2784
+
2785
+ # Check that sys.path of the non-virtualenv appears
2786
+ # in the sys.path of the virtualenv
2787
+ #
2788
+ # Get the last line (which contains the printed sys.path); convert
2789
+ # back into a list
2790
+ assert all(path in venv_sys_path for path in no_venv_sys_path)
2791
+
2792
+ # Check that the virtualenv directory appears in the sys.path of
2793
+ # the virtualenv, but not in the sys.path of the non-virtualenv
2794
+ assert not any(env_dir in path for path in no_venv_sys_path)
2795
+ assert any(env_dir in path for path in venv_sys_path)
2796
+
2797
+
2737
2798
  # TODO: test timeit, time
2738
2799
  # TODO: test --attach
2739
2800
  # TODO: test postmortem debugging
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes