Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leave script + ancestor checking #11

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Automatically execution of `.autoxsh` xonsh script after entering (cd-ing) into the directory.

[![PyPi version](https://img.shields.io/pypi/v/xonsh-autoxsh.svg?style=flat-square)](https://pypi.python.org/pypi/xonsh-autoxsh) [![PyPi license](https://img.shields.io/pypi/l/xonsh-autoxsh.svg?style=flat-square)](https://pypi.python.org/pypi/xonsh-autoxsh) [![PyPi license](https://img.shields.io/pypi/pyversions/xonsh-autoxsh.svg?style=flat-square)](https://pypi.python.org/pypi/xonsh-autoxsh)
[![PyPi version](https://img.shields.io/pypi/v/xonsh-autoxsh.svg?style=flat-square)](https://pypi.python.org/pypi/xonsh-autoxsh) [![PyPi license](https://img.shields.io/pypi/l/xonsh-autoxsh.svg?style=flat-square)](https://pypi.python.org/pypi/xonsh-autoxsh) [![Python version](https://img.shields.io/pypi/pyversions/xonsh-autoxsh.svg?style=flat-square)](https://pypi.python.org/pypi/xonsh-autoxsh)

## Installation
```python
Expand All @@ -11,32 +11,79 @@ pip install xonsh-autoxsh
echo 'xontrib load autoxsh' >> ~/.xonshrc
```

## Autoxsh files

- `.autoxsh` - executed after entering a directory
- `.enter.xsh` - same as `.autoxsh` but with `.xsh` extension
- `.leave.xsh` - executed after leaving a directory

Execution order: `(olddir)/.leave.xsh`, `(newdir)/.autoxsh`, `(newdir)/.enter.xsh`.

## Environment variables

- `$AXSH_CHECK_PARENTS` - enable checking the parents of the old / new directory for leave / enter files
- `$AXSH_DEBUG` - prints debug information

## Use cases

### Run xonsh script after entering (cd-ing) into the directory

```python
mkdir -p /tmp/dir
echo "print('it works!')" > /tmp/dir/.autoxsh
echo "print('it works!')" > /tmp/dir/.enter.xsh
cd /tmp/dir
# Unauthorized ".autoxsh" file found in this directory. Authorize and invoke? (y/n/ignore): y
# Unauthorized ".enter.xsh" file found in this directory. Authorize and invoke? (y/n/ignore): y
# it works!
cd /
cd /tmp/dir
# it works!
```

### Run xonsh script after leaving a directory

```python
mkdir -p /tmp/dir
echo "print('bye!')" > /tmp/dir/.leave.xsh
cd /tmp/dir
cd /
# Unauthorized ".leave.xsh" file found in this directory. Authorize and invoke? (y/n/ignore): y
# bye!
cd /tmp/dir
cd /
# bye!
```

### Activate Python virtual environment with vox

```python
xontrib load vox
vox new myenv
mkdir -p /tmp/dir
echo "vox activate myenv" > /tmp/dir/.autoxsh
echo "vox activate myenv" > /tmp/dir/.enter.xsh
cd /tmp/dir
# Activated "myenv".
```

### Parent check mode

```python
mkdir -p /tmp/dir1/sdir1
mkdir -p /tmp/dir2
echo "hello dir1" > /tmp/dir1/.enter.xsh
echo "bye dir1" > /tmp/dir1/.leave.xsh
echo "hello sdir1" > /tmp/dir1/sdir1/.enter.xsh
echo "hello dir2" > /tmp/dir1/.erter.xsh
cd /tmp/dir1/sdir1
# (authorization ignored)
# hello dir1
# hello sdir1
cd /tmp/dir2
# bye dir1
# hello dir2
```

Please note that `.autoxsh` is ignored in parent check mode, only `.enter.xsh` and `.leave.xsh` scripts are executed.

## Links
* This package is the part of [ergopack](https://github.com/anki-code/xontrib-ergopack) - the pack of ergonomic xontribs.
* This package was created with [xontrib cookiecutter template](https://github.com/xonsh/xontrib-cookiecutter).
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
'Intended Audience :: End Users/Desktop',
'Operating System :: OS Independent',
'Programming Language :: Python',
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
'Topic :: Desktop Environment',
'Topic :: System :: Shells',
'Topic :: System :: System Shells',
]
]
)
64 changes: 55 additions & 9 deletions xontrib/autoxsh.xsh
Original file line number Diff line number Diff line change
@@ -1,31 +1,73 @@
import os
from pathlib import Path
_AUTHORIZED_FILE = os.path.join(__xonsh__.env['XONSH_DATA_DIR'], 'xontrib_autoxsh_authorized.txt')
_IGNORE_FILE = os.path.join(__xonsh__.env['XONSH_DATA_DIR'], 'xontrib_autoxsh_ignore.txt')
_ENTER_FILE = '.enter.xsh'
_LEAVE_FILE = '.leave.xsh'


# create auth / ignore files
open(_AUTHORIZED_FILE, 'a').close()
open(_IGNORE_FILE, 'a').close()


@events.on_chdir
def auto_cd(olddir, newdir, **kw):
target = os.path.join(newdir, '.autoxsh')
target = os.path.expanduser(target)
has_envfile = os.path.isfile(target)
if not has_envfile:
olddir = Path(olddir).expanduser().resolve()
newdir = Path(newdir).expanduser().resolve()
commonpath = Path(os.path.commonpath([olddir, newdir]))

tlist = []

if __xonsh__.env.get('AXSH_CHECK_PARENTS', False):
# directories left
if olddir > commonpath:
tlist += [(olddir, _LEAVE_FILE)]

tlist += [(parent, _LEAVE_FILE) for parent in olddir.parents if parent > commonpath]

# directories entered
tlist += [(parent, _ENTER_FILE) for parent in reversed(newdir.parents) if parent > commonpath]

if newdir > commonpath:
tlist += [(newdir, _ENTER_FILE)]
else:
tlist += [(olddir, _LEAVE_FILE), (newdir, '.autoxsh'), (newdir, _ENTER_FILE)]

for tdir, file in tlist:
run_script(tdir, file)


def run_script(tdir, file):
debug = __xonsh__.env.get('AXSH_DEBUG', False)

target = tdir / file

if debug:
printx(f'{{YELLOW}}[axsh]{{RESET}} checking file: {target}')

if not os.path.isfile(target):
return
# Deal with authorization
open(_AUTHORIZED_FILE, 'a').close()
open(_IGNORE_FILE, 'a').close()

if debug:
printx(f'{{YELLOW}}[axsh]{{RESET}} ... file found')

# check whether dir is ignored
with open(_IGNORE_FILE, 'r') as ignore_file:
for line in ignore_file.readlines():
if target in line:
return

# check whether dir is authorized
authorized = False
with open(_AUTHORIZED_FILE, 'r') as trust_file:
for line in trust_file.readlines():
if target in line:
if str(target) in line:
authorized = True
break

if not authorized:
printx('{YELLOW}Unauthorized ".autoxsh" file found in this directory. Authorize and invoke? (y/n/ignore): {RESET}', end='')
printx(f'{{INTENSE_YELLOW}}[axsh]{{RESET}} Unauthorized "{{INTENSE_RED}}{file}{{RESET}}" file found in this directory. Authorize and invoke? (y/n/ignore): ', end='')
to_authorize = input().lower()
if "y" in to_authorize:
authorized = True
Expand All @@ -34,8 +76,12 @@ def auto_cd(olddir, newdir, **kw):
if "ignore" in to_authorize:
with open(_IGNORE_FILE, 'a') as ignore_file:
ignore_file.write('{}\n'.format(target))

if authorized:
if debug:
printx(f'{{YELLOW}}[axsh]{{RESET}} executing')
source @(target)


__all__ = ()
__version__ = 0.4