Repository: hylang/hy

Issue:
Cannot `require` module from a package
 The following are all valid:
```hy
$ > hy --spy --repl-output-fn=hy.contrib.pprint.pformat
import hy
hy 0.20.0+132.g2a6b79f1 using CPython(heads/makepkg-dirty:ba251c2ae66) 3.10.0a6+ on Linux
=> (require hy.contrib.walk)
hy.macros.require('hy.contrib.walk', None, assignments='ALL', prefix='hy.contrib.walk')
None
=> (import hy.contrib.walk)
import hy.contrib.walk
None
=> (require [hy.contrib.walk :as w])
hy.macros.require('hy.contrib.walk', None, assignments='ALL', prefix='w')
None
=> (import [hy.contrib.walk :as w])
import hy.contrib.walk as w
None
=> (import [hy.contrib [walk]])
from hy.contrib import walk
None
```
However, the following fails:
```hy
=> (require [hy.contrib [walk]])
Traceback (most recent call last):
  File "stdin-fc566da3a366533698786e10deb0bc456fb8d19e", line 1, in <module>
    (require [hy.contrib [walk]])
hy.errors.HyRequireError: The module <module 'hy.contrib' from '/home/peter/Programming/hy/hy-source/hy/hy/contrib/__init__.py'> has no macros
```
As I understand, `import` and `require` should allow for the same syntax, and given that the previous `require` forms work as expected, I think that the last one should as well.

Just to clarify, I think that `(require [hy.contrib [walk]])` should have the same effect as `(require [hy.contrib.walk :as walk])`.


Discussion:
You know, I don't think I knew that `from a.b import c` is legal Python even when `a.b.c` is a module instead of an object that exists in the `a.b` module.
what should the behavior be if `hy.contrib` has a macro also named `walk`? I think python checks the module for variables first then goes to sub modules, but i'd need to double check that. 
Assuming that you have the following scenario:
```
foo
foo/__init__.py
foo/bar.py
```
Then `from foo import bar` will first check for any object `bar` in `foo/__init__.py` and if that fails then look `foo/bar.py`.

Resolving patch:
diff --git a/hy/macros.py b/hy/macros.py
--- a/hy/macros.py
+++ b/hy/macros.py
@@ -156,7 +156,9 @@ def require(source_module, target_module, assignments, prefix=""):
                 source_dirs = source_module.split(".")
                 target_dirs = (getattr(target_module, "__name__", target_module)
                                .split("."))
-                while source_dirs and target_dirs and source_dirs[0] == "":
+                while (len(source_dirs) > 1
+                       and source_dirs[0] == ""
+                       and target_dirs):
                     source_dirs.pop(0)
                     target_dirs.pop()
                 package = ".".join(target_dirs + source_dirs[:-1])
@@ -170,8 +172,17 @@ def require(source_module, target_module, assignments, prefix=""):
 
     if not source_module.__macros__:
         if assignments != "ALL":
-            raise HyRequireError('The module {} has no macros'.format(
-                source_module))
+            for name, alias in assignments:
+                try:
+                    require(f"{source_module.__name__}.{mangle(name)}",
+                            target_module,
+                            "ALL",
+                            prefix=alias)
+                except HyRequireError as e:
+                    raise HyRequireError(f"Cannot import name '{name}'"
+                                         f" from '{source_module.__name__}'"
+                                         f" ({source_module.__file__})")
+            return True
         else:
             return False
 


Test patch:
diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy
--- a/tests/native_tests/language.hy
+++ b/tests/native_tests/language.hy
@@ -1463,7 +1463,14 @@ cee\"} dee" "ey bee\ncee dee"))
   (require [tests.resources.tlib [*]])
   (assert (= (parald 1 2 3) [9 1 2 3]))
   (assert (= (✈ "silly") "plane silly"))
-  (assert (= (hyx_XairplaneX "foolish") "plane foolish")))
+  (assert (= (hyx_XairplaneX "foolish") "plane foolish"))
+
+  (require [tests.resources [tlib macros :as m]])
+  (assert (in "tlib.qplah" __macros__))
+  (assert (in (mangle "m.test-macro") __macros__))
+  (require [os [path]])
+  (with [(pytest.raises hy.errors.HyRequireError)]
+    (hy.eval '(require [tests.resources [does-not-exist]]))))
 
 
 (defn test-require-native []
@@ -1479,11 +1486,14 @@ cee\"} dee" "ey bee\ncee dee"))
   (assert (= x [3 2 1])))
 
 (defn test-relative-require []
-  (require [..resources.macros [nonlocal-test-macro]])
-  (assert (in "nonlocal_test_macro" __macros__))
+  (require [..resources.macros [test-macro]])
+  (assert (in "test_macro" __macros__))
 
   (require [.native-macros [rev]])
-  (assert (in "rev" __macros__)))
+  (assert (in "rev" __macros__))
+
+  (require [. [native-macros :as m]])
+  (assert (in "m.rev" __macros__)))
 
 
 (defn test-encoding-nightmares []