Skip to content

Commit

Permalink
added venvlink-autoenv support
Browse files Browse the repository at this point in the history
  • Loading branch information
Niko Pasanen committed Dec 29, 2020
1 parent bccac6c commit 871c963
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 31 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ Therefore, you can use `venvlink` on `Python 3.6.4 32-bit` and `Python 3.9.2 64-

and the rest you can use normally. Note that virtual environments in the centralized folder will have the same python version which was used to create the virtual environment in the first place. You can later on use your main python executable to create more links for an existing virtual environment, no matter what version of python it is using. Use descriptive venv names to distinguish the venvs with different python versions, if needed.

### 🚫🔮 no magic
### ✨🔥 auto-activate

`venvlink` is a really simple tool. All it does is
- Runs `python -m venv projname`, inside the centralized venv folder
- Creates `activate` scripts inside your project dir, that act as proxies ("links") for the real activate scripts.
The `venvlink` supports a Powershell addon called [venvlink-autoenv](https://github.com/np-8/venvlink-autoenv-powershell) which will automatically activate/deactivate the virtual environment on Windows Powershell! *(New in version 0.5.0)*

In addition there are some convenience scripts, for removing a venv or overwriting a proxy activate script. But that's about it.
<p align="center">
<img src="docs/example-usage.gif" width="450">
</p>

# Installing
### Requirements
Expand Down
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.5.0 (2020-12-29)

- Added support for [venvlink-autoenv](https://github.com/np-8/venvlink-autoenv-powershell) -> Possible to automatically activate/deactivate virtual environments in Windows Powershell.
Binary file added docs/example-usage.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 49 additions & 23 deletions venvlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,35 @@ def get_from_venv_dir(venv_dir, file="pyvenv.cfg"):
return venv_dir / file


def get_venvlink_text(venv_project):
text = f"The virtual environment is located at {venv_project}" + os.linesep
text += f"Created with venvlink v.{__version__}"
def get_venvlink_header(entering=True, comment_symbol='#', shell='Powershell'):
tmp = 'entered' if entering else 'left'
text = f"{comment_symbol} This file is automatically generated with venvlink v.{__version__}\n"
text += f"{comment_symbol} The contents of this file are automatically executed\n"
text += f"{comment_symbol} when the project folder is {tmp} with {shell}.\n"
return text


def get_venvlink_env_text():
text = get_venvlink_header(entering=True, comment_symbol='#')
text += '\n' + r'& "$workdir\Scripts\Activate.ps1"'
return text


def get_venvlink_leave_text():
text = get_venvlink_header(entering=False, comment_symbol='#')
text += "\ntry { & deactivate }\n"
text += "catch { }"
return text


def get_activate_ps_text(venvdir_src, real_activate_ps1):
return f"""
Write-Host "[venvlink]" -NoNewline -ForegroundColor Cyan
Write-Host " activate " -NoNewline -ForegroundColor White
Write-Host "{venvdir_src}" -NoNewline -ForegroundColor White
& '{real_activate_ps1}'"""


class VenvLink:
def __init__(self, config_file=None):
config_file = Path(config_file) if config_file is not None else None
Expand Down Expand Up @@ -96,16 +119,17 @@ def create_venv(self, project_name, workdir, system_site_packages=False):
# Create the virtual environment in the "centralized location"
# Note: project_name may change
project_name = self._create_venv_to_venv_folder(
project_name, system_site_packages=system_site_packages
)
project_name, system_site_packages=system_site_packages)
except UserAborted:
print("Canceled.")
return

# Create the "proxied" virtual environment in the workdir
self._create_venv_to_workdir(workdir, project_name)

def _create_venv_to_venv_folder(self, project_name, system_site_packages=False):
def _create_venv_to_venv_folder(self,
project_name,
system_site_packages=False):

# Create the folder for all the virtual environments
self.venv_folder.mkdir(exist_ok=True, parents=True)
Expand All @@ -120,9 +144,8 @@ def _create_venv_to_venv_folder(self, project_name, system_site_packages=False):
subprocess_cmd = [sys.executable, "-m", "venv", project_name]
if system_site_packages:
subprocess_cmd.append("--system-site-packages")
logger.info(
'Running: "%s" with cwd="%s"', " ".join(subprocess_cmd), self.venv_folder
)
print('Running: "{}" with cwd="{}"'.format(" ".join(subprocess_cmd),
self.venv_folder))
subprocess.run(subprocess_cmd, cwd=self.venv_folder)
return project_name

Expand Down Expand Up @@ -172,7 +195,8 @@ def _when_venv_exists_already(self, project_name):
print(text)

prompt = "[Y] Yes [N] No, give new name. [A] Abort: "
validator = partial(is_in_accepted_values, accepted_values={"Y", "N", "A"})
validator = partial(is_in_accepted_values,
accepted_values={"Y", "N", "A"})
value = get_input(prompt, validator).upper()

if value == "Y":
Expand All @@ -182,7 +206,10 @@ def _when_venv_exists_already(self, project_name):
elif value == "A":
raise UserAborted()

def _check_no_venv_in_workdir(self, workdir, project_name, venvname="venv"):
def _check_no_venv_in_workdir(self,
workdir,
project_name,
venvname="venv"):

venvdir_dst = workdir / venvname
if venvdir_dst.exists():
Expand All @@ -192,7 +219,8 @@ def _check_no_venv_in_workdir(self, workdir, project_name, venvname="venv"):
)

prompt = "[Y] Yes [A] Abort: "
validator = partial(is_in_accepted_values, accepted_values={"Y", "A"})
validator = partial(is_in_accepted_values,
accepted_values={"Y", "A"})
value = get_input(prompt, validator).upper()

if value == "A":
Expand Down Expand Up @@ -222,18 +250,13 @@ def _create_venv_to_workdir(self, workdir, project_name, venvname="venv"):
files_and_contents = (
(
"Scripts" + os.path.sep + "Activate.ps1",
(
f"""Write-Output 'venvlink: Activating virtual env in "{venvdir_src}"'\n"""
f"& '{real_activate_ps1}'"
),
get_activate_ps_text(venvdir_src, real_activate_ps1),
),
(
"Scripts" + os.path.sep + "activate.bat",
(
"@echo off\n"
f"""echo venvlink: Activating virtual env in \"{venvdir_src}\"\n"""
f"""call \"{real_activate_bat}\""""
),
("@echo off\n"
f"""echo venvlink: Activating virtual env in \"{venvdir_src}\"\n"""
f"""call \"{real_activate_bat}\""""),
), # Dummy file which makes possible to run simply "venv\Scripts\activate"
("Scripts" + os.path.sep + "activate", ""),
)
Expand All @@ -244,8 +267,11 @@ def _create_venv_to_workdir(self, workdir, project_name, venvname="venv"):
with open(file_dst, "w", encoding="utf-8") as f:
print(content, file=f)

with open(venvdir_dst / "venvlink", "w") as f:
print(get_venvlink_text(venvdir_src), file=f)
with open(venvdir_dst / "venvlink-autoenv.ps1", "w") as f:
print(get_venvlink_env_text(), file=f, end='')

with open(venvdir_dst / "venvlink-autoenv.leave.ps1", "w") as f:
print(get_venvlink_leave_text(), file=f, end='')

@property
def venv_folder(self):
Expand Down
6 changes: 4 additions & 2 deletions venvlink/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from venvlink.config import init_config
from venvlink.__version__ import __version__

parser = argparse.ArgumentParser(prog="venvlink", description=f"venvlink {__version__}")
parser = argparse.ArgumentParser(prog="venvlink",
description=f"venvlink {__version__}")

parser.add_argument(
"--init",
Expand All @@ -18,7 +19,8 @@
parser.add_argument(
"-d",
"--delete",
help="Delete the virtual environment associated with project_name (instead of creating)",
help=
"Delete the virtual environment associated with project_name (instead of creating)",
action="store_true",
default=False,
)
Expand Down
2 changes: 1 addition & 1 deletion venvlink/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.0"
__version__ = "0.5.0"

0 comments on commit 871c963

Please sign in to comment.