Here is a patch that makes gnulib-tool.py use the standard Python program
directory structure:
  - a single .py file in the parent directory,
  - any number of .py files in a subdirectory.

With this, no PYTHONPATH override is needed any more, and the import-related
error markers in Eclipse are gone.

Since this change has consequences on the Eclipse project configuration, I
incorporated the instructions from
<https://lists.gnu.org/archive/html/bug-gnulib/2024-03/msg00157.html>,
with the necessary changes, into the HACKING file. Collin, feel free
to document the way you install, configure, and customize PyCharm in
the same file.

Bruno

>From dbeb5f1f1a018b6a81f899092554ff0af8776ec3 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 1 Apr 2024 21:50:51 +0200
Subject: [PATCH 1/2] gnulib-tool.py: Use a standard Python program directory
 structure.

* pygnulib/main.py (main_with_exception_handling): New function for
existing code.
* .gnulib-tool.py: New file.
* pygnulib/constants.py: Update the computation of APP['root'].
* gnulib-tool.py: Don't set PYTHONPATH.
---
 .gnulib-tool.py       | 30 ++++++++++++++++++++++++++++++
 ChangeLog             |  9 +++++++++
 gnulib-tool.py        |  6 +-----
 pygnulib/constants.py |  4 ++--
 pygnulib/main.py      |  2 +-
 5 files changed, 43 insertions(+), 8 deletions(-)
 create mode 100644 .gnulib-tool.py

diff --git a/.gnulib-tool.py b/.gnulib-tool.py
new file mode 100644
index 0000000000..3b2d6913f3
--- /dev/null
+++ b/.gnulib-tool.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 Free Software Foundation, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+# This file exists to satisfy the standard layout of a Python program:
+#   - a single .py file in the parent directory,
+#   - any number of .py files in a subdirectory.
+# Explanation: https://stackoverflow.com/questions/16981921/relative-imports-in-python-3
+# Without this standard layout, we got
+#   - errors at runtime:
+#     from . import constants          =>   ImportError: attempted relative import with no known parent package
+#     from pygnulib import constants   =>   ModuleNotFoundError: No module named 'pygnulib'
+#   - error markers in Eclipse + PyDev.
+
+from pygnulib import main
+
+if __name__ == '__main__':
+    main.main_with_exception_handling()
diff --git a/ChangeLog b/ChangeLog
index ef529a5e4c..7a387b3300 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2024-04-01  Bruno Haible  <br...@clisp.org>
+
+	gnulib-tool.py: Use a standard Python program directory structure.
+	* pygnulib/main.py (main_with_exception_handling): New function for
+	existing code.
+	* .gnulib-tool.py: New file.
+	* pygnulib/constants.py: Update the computation of APP['root'].
+	* gnulib-tool.py: Don't set PYTHONPATH.
+
 2024-04-01  Bruno Haible  <br...@clisp.org>
 
 	gnulib-tool.py: Simplify imports.
diff --git a/gnulib-tool.py b/gnulib-tool.py
index f94c9c36a7..bb2837a3d5 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -144,8 +144,4 @@ else
   func_fatal_error "python3 not found; try setting GNULIB_TOOL_IMPL=sh"
 fi
 
-# Setting the PYTHONPATH environment variable is needed to avoid an error:
-#   from . import constants          =>   ImportError: attempted relative import with no known parent package
-#   from pygnulib import constants   =>   ModuleNotFoundError: No module named 'pygnulib'
-# Explanation: https://stackoverflow.com/questions/16981921/relative-imports-in-python-3
-exec env PYTHONPATH="$gnulib_dir" python3 "$gnulib_dir/pygnulib/main.py" "$@"
+exec python3 "$gnulib_dir/.gnulib-tool.py" "$@"
diff --git a/pygnulib/constants.py b/pygnulib/constants.py
index a6ef32cb47..8f698fe01e 100644
--- a/pygnulib/constants.py
+++ b/pygnulib/constants.py
@@ -74,8 +74,8 @@ if ENCS['shell'] == None:
     ENCS['shell'] = 'UTF-8'
 
 # Set APP dictionary
-APP['path'] = os.path.realpath(sys.argv[0])                 # file name of <gnulib>/pygnulib/main.py
-APP['root'] = os.path.dirname(os.path.dirname(APP['path'])) # file name of <gnulib>
+APP['path'] = os.path.realpath(sys.argv[0])                 # file name of <gnulib>/.gnulib-tool.py
+APP['root'] = os.path.dirname(APP['path'])                  # file name of <gnulib>
 APP['name'] = os.path.join(APP['root'], 'gnulib-tool.py')
 
 # Set DIRS directory
diff --git a/pygnulib/main.py b/pygnulib/main.py
index 31bb434b3a..a57e5b04a3 100644
--- a/pygnulib/main.py
+++ b/pygnulib/main.py
@@ -1373,7 +1373,7 @@ def main() -> None:
                 pass
 
 
-if __name__ == '__main__':
+def main_with_exception_handling() -> None:
     try:  # Try to execute
         main()
     except GLError as error:
-- 
2.34.1

>From 382ba5c05473d4d8c450e1eef055f86b5b27fbba Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 2 Apr 2024 00:21:54 +0200
Subject: [PATCH 2/2] gnulib-tool.py: Add developer documentation.

* HACKING: New section "Debugging the Python implementation of
gnulib-tool".
---
 ChangeLog |  6 ++++++
 HACKING   | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 7a387b3300..1668b7cfd8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2024-04-01  Bruno Haible  <br...@clisp.org>
+
+	gnulib-tool.py: Add developer documentation.
+	* HACKING: New section "Debugging the Python implementation of
+	gnulib-tool".
+
 2024-04-01  Bruno Haible  <br...@clisp.org>
 
 	gnulib-tool.py: Use a standard Python program directory structure.
diff --git a/HACKING b/HACKING
index a3c65213d2..119bac8c10 100644
--- a/HACKING
+++ b/HACKING
@@ -29,6 +29,7 @@ Using git
     Copyright-paperwork-exempt: Yes
   - use the 'git commit' option --author="Contributor Name <email@address>"
 
+
 License Notices
 ===============
 
@@ -43,12 +44,14 @@ use the license notices from etc/license-notices/ . This avoids gratuitous
 differences in wording, as well misunderstandings when a license notice
 would say "This program ...".
 
+
 Test Suite
 ==========
 
 When adding a module, add a unit test module as well.  This is our best
 chance to catch portability problems.
 
+
 Warning Options
 ===============
 
@@ -514,3 +517,55 @@ https://releases.llvm.org/16.0.0/tools/clang/docs/DiagnosticsReference.html :
   $ make
 Again, use your own judgement to determine whether to fix or ignore a
 specific warning.
+
+
+Debugging the Python implementation of gnulib-tool
+==================================================
+
+With Eclipse and PyDev as IDE
+-----------------------------
+
+* Download and configuration:
+  - Eclipse IDE from https://www.eclipse.org/downloads/packages/
+    (either the build for Java or for C/C++ should work fine;
+    either use the Eclipse Installer program at the top of the page,
+    or one of the individual download links below).
+  - PyDev from https://www.pydev.org/download.html
+    section "Install as Plugin".
+    (Don't use LiClipse, since the license costs money.)
+  - Follow https://www.pydev.org/manual_101_install.html,
+    replacing http://www.pydev.org/updates
+    with      https://www.pydev.org/updates
+  - Once installed, restart the IDE.
+  - Window > Preferences > PyDev > Interpreters > Python Interpreter:
+    New > path: /usr/bin/python3
+
+* Create a project:
+  Let GNULIB_DIR be my gnulib checkout.
+  - File > New > Project... > PyDev > PyDev Project. Call it 'gnulib-tool'.
+    Note that this is a project inside the Eclipse workspace, so far
+    unrelated to the GNULIB_DIR.
+  - Popup menu > New > Link to Existing Source
+    Name: gnulib
+    Path: <GNULIB_DIR>
+
+* Create a run configuration:
+  - Run > Run Configurations... > Python Run
+    Popup menu > New configuration
+      Name: gnulib-tool.py
+      Main > Project: gnulib-tool
+      Main > Main Module: ${workspace_loc:gnulib-tool/gnulib/.gnulib-tool.py}
+      Arguments > Program arguments: --help
+  - Test it: Run this configuration.
+
+* Create a debug configuration:
+  - Run > Debug Configurations... > gnulib-tool.py
+    Popup menu > Duplicate
+  - In the duplicate, set
+    Arguments > Working directory: /tmp/oath-toolkit/lib
+    Arguments > Program arguments: --add-import
+
+* Debug it:
+  - Open GLImport.py.
+  - On the left-hand border of this editor, do "Add breakpoint".
+  - Run > Debug Configurations... > pick the duplicate. Press Debug.
-- 
2.34.1

Reply via email to