Repository: hylang/hy

Issue:
`__file__` is sometimes missing the directory
```sh
$ cd /tmp
$ echo 'print(__file__)' >a.py ; python3 a.py
/tmp/a.py
$ echo '(print __file__)' >b.hy ; hy b.hy
b.hy
```

The variable seems to be set correctly when the file is imported rather than run directly:

```sh
$ hy -c '(import a b) (print a.__file__ b.__file__)'
/tmp/a.py
/tmp/b.hy
/tmp/a.py /tmp/b.hy
```


Discussion:

Resolving patch:
diff --git a/hy/cmdline.py b/hy/cmdline.py
--- a/hy/cmdline.py
+++ b/hy/cmdline.py
@@ -8,6 +8,7 @@
 import io
 import linecache
 import os
+import platform
 import py_compile
 import runpy
 import sys
@@ -15,6 +16,7 @@
 import traceback
 import types
 from contextlib import contextmanager
+from pathlib import Path
 
 import hy
 from hy._compat import PY3_9
@@ -404,8 +406,6 @@ def runsource(self, source, filename="<stdin>", symbol="exec"):
     def run(self):
         "Start running the REPL. Return 0 when done."
 
-        import platform
-
         import colorama
 
         sys.ps1 = "=> "
@@ -438,11 +438,9 @@ def run(self):
 def set_path(filename):
     """Emulate Python cmdline behavior by setting `sys.path` relative
     to the executed file's location."""
-    path = os.path.realpath(os.path.dirname(filename))
     if sys.path[0] == "":
-        sys.path[0] = path
-    else:
-        sys.path.insert(0, path)
+        sys.path.pop(0)
+    sys.path.insert(0, str(Path(filename).parent.resolve()))
 
 
 def run_command(source, filename=None):
@@ -465,7 +463,7 @@ def run_command(source, filename=None):
 
 
 def run_icommand(source, **kwargs):
-    if os.path.exists(source):
+    if Path(source).exists():
         filename = source
         set_path(source)
         with open(source, "r", encoding="utf-8") as f:
@@ -681,13 +679,20 @@ def proc_opt(opt, arg=None, item=None, i=None):
 
         else:
             # User did "hy <filename>"
-            filename = argv[0]
+
+            filename = Path(argv[0])
             set_path(filename)
+            # Ensure __file__ is set correctly in the code we're about
+            # to run.
+            if PY3_9 and not filename.is_absolute():
+                filename = Path.cwd() / filename
+            if PY3_9 and platform.system() == "Windows":
+                filename = os.path.normpath(filename)
 
             try:
                 sys.argv = argv
                 with filtered_hy_exceptions():
-                    runhy.run_path(filename, run_name="__main__")
+                    runhy.run_path(str(filename), run_name="__main__")
                 return 0
             except FileNotFoundError as e:
                 print(


Test patch:
diff --git a/tests/test_bin.py b/tests/test_bin.py
--- a/tests/test_bin.py
+++ b/tests/test_bin.py
@@ -3,13 +3,17 @@
 
 import builtins
 import os
+import platform
 import re
 import shlex
 import subprocess
 from importlib.util import cache_from_source
+from pathlib import Path
 
 import pytest
 
+from hy._compat import PY3_9
+
 
 def pyr(s=""):
     return "hy --repl-output-fn=repr " + s
@@ -654,3 +658,36 @@ def test_output_buffering(tmp_path):
             _, stderr = run_cmd(f"hy {flag} {pf}", stdout=o)
         assert stderr.strip() == expected
         assert tf.read_text().splitlines() == ["line 1", "line 2"]
+
+
+def test_uufileuu(tmp_path, monkeypatch):
+    # `__file__` should be set the same way as in Python.
+    # https://github.com/hylang/hy/issues/2318
+
+    (tmp_path / "realdir").mkdir()
+    (tmp_path / "realdir" / "hyex.hy").write_text('(print __file__)')
+    (tmp_path / "realdir" / "pyex.py").write_text('print(__file__)')
+
+    def file_is(arg, expected_py3_9):
+        expected = expected_py3_9 if PY3_9 else Path(arg)
+        output, _ = run_cmd("python3 " + shlex.quote(arg + "pyex.py"))
+        assert output.rstrip() == str(expected / "pyex.py")
+        output, _ = run_cmd("hy " + shlex.quote(arg + "hyex.hy"))
+        assert output.rstrip() == str(expected / "hyex.hy")
+
+    monkeypatch.chdir(tmp_path)
+    file_is("realdir/", tmp_path / "realdir")
+
+    monkeypatch.chdir(tmp_path / "realdir")
+    file_is("", tmp_path / "realdir")
+
+    (tmp_path / "symdir").symlink_to("realdir", target_is_directory = True)
+    monkeypatch.chdir(tmp_path)
+    file_is("symdir/", tmp_path / "symdir")
+
+    (tmp_path / "realdir" / "child").mkdir()
+    monkeypatch.chdir(tmp_path / "realdir" / "child")
+    file_is("../",
+        tmp_path / "realdir"
+        if platform.system() == "Windows" else
+        tmp_path / "realdir" / "child" / "..")