Repository: hylang/hy

Issue:
Init script
It would be nice to have an init script analogous to `$PYTHONSTARTUP` as many lisps have this feature. It's location should be, in keeping with the XDG spec, `$XDG_CONFIG_HOME/hy/init.hy`.


Discussion:
That's an interesting idea.  What would a script like that be used for in practice?
Just some thoughts from the top of my mind:

For lisps in general:
- Customizing the prompt
- (Re-)defining variables and functions
- Installing and importing packages
- Special behaviour when run inside Emacs
- Loading REPL-enhancing libraries
- Reacting to custom commandline arguments and environment variables

For Hy in particular:
- Setting `spy` and `repl-output-fn` (if that's possible)
- Creating a default pretty printer using `pprint`
- Manipulating `sys.path`
- Printing the current `venv`
@alphapapa This is my init script. I load it in Emacs Lisp code.

```
((fn []
  (import multiprocessing)
  (multiprocessing.set-executable "/usr/bin/python3")))

(require [kodhy.macros [*]])

(import
  os sys re
  [math [*]]
  [collections [Counter]]
  [numpy :as np]
  [scipy.stats :as scist]
  [pandas :as pd]
  [matplotlib.pyplot :as plt]
  [kodhy.util [*]])

(plt.ion)

(pd.set-option "display.width" (int 68))
(pd.set-option "display.max_columns" (int 50))
```

@Janfel

> Setting `spy` and `repl-output-fn` (if that's possible)

Not without monkey-patching `HyREPL`.
It seems pretty easy to get something like this working. A quick proof of concept shows that this seems to work fine when inserted in the `HyREPL` class right after the `self.locals = ...` line. Am I missing something with this? My gut says this should be more tricky :/

```python
if os.environ.get("HYSTARTUP"):                                           
    try:                                                                  
        mod = hy.importer._import_from_path("hystartup", os.environ.get("HYSTARTUP")) 
        imports = mod.__dict__.get(                                       
            '__all__',                                                    
            [name for name in mod.__dict__ if not name.startswith("_")]   
        )                                                                 
        # Load imports and defs                                           
        self.locals.update({name: mod.__dict__[name] for name in imports})
                                                                          
        # load module macros                                              
        require(mod, self.module, assignments='ALL')                      
    except Exception as e:                                                
        print(e)                                                          
```
That does seem to cover everything. If you can't find any bugs, then PR that mofo.
radical. Before I do though, this also seems to cover the setting of `spy` and `output_fn` if we add this just after the `imports = ...` line:

```python
spy = imports.pop("repl_spy", spy)
output_fn = imports.pop("repl_output_fn", output_fn)
```
are we okay with this little bit of monkey patching?

Yeah, I wouldn't even call that monkey-patching, since it will be in a method of `HyREPL`.
So it looks like this is working fine for everything *but* tags and macros. Macros reqluired from other files are fine, but those defined in the init script fail.
Macros with:
```python
'NoneType' object has no attribute '__dict__'
```

and tags with:
```python
'NoneType' object has no attribute '__name__'
```

I've tried importing the module in a few different ways, but no luck so far. Any ideas? I'm not super well versed with how the Hy importer does it's magic.
Never mind, going through `HyLoader` seemed to work :woman_shrugging: 

Resolving patch:
diff --git a/hy/cmdline.py b/hy/cmdline.py
--- a/hy/cmdline.py
+++ b/hy/cmdline.py
@@ -35,7 +35,7 @@
                          hy_ast_compile_flags)
 from hy.errors import (HyLanguageError, HyRequireError, HyMacroExpansionError,
                        filtered_hy_exceptions, hy_exc_handler)
-from hy.importer import runhy
+from hy.importer import runhy, HyLoader
 from hy.completer import completion, Completer
 from hy.macros import macro, require
 from hy.models import HyExpression, HyString, HySymbol
@@ -243,6 +243,25 @@ def __init__(self, spy=False, output_fn=None, locals=None,
         self.module.__dict__.update(self.locals)
         self.locals = self.module.__dict__
 
+        if os.environ.get("HYSTARTUP"):
+            try:
+                loader = HyLoader("__hystartup__", os.environ.get("HYSTARTUP"))
+                mod = loader.load_module()
+                imports = mod.__dict__.get(
+                    '__all__',
+                    [name for name in mod.__dict__ if not name.startswith("_")]
+                )
+                imports = {name: mod.__dict__[name] for name in imports}
+                spy = imports.get("repl_spy", spy)
+                output_fn = imports.get("repl_output_fn", output_fn)
+
+                # Load imports and defs
+                self.locals.update(imports)
+
+                # load module macros
+                require(mod, self.module, assignments='ALL')
+            except Exception as e:
+                print(e)
         # Load cmdline-specific macros.
         require('hy.cmdline', self.module, assignments='ALL')
 


Test patch: