Skip to content

Commit

Permalink
Merge pull request #15 from karkason/feature/support-venvs
Browse files Browse the repository at this point in the history
Feature/support venvs
  • Loading branch information
karkason authored Sep 1, 2022
2 parents 1316bd0 + 3d8ae44 commit 85d82fd
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 16 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/publish_to_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on: push
jobs:
build-n-publish:
name: Publish Python distributions to PyPI
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@master
- name: Set up Python 3.7
Expand All @@ -21,13 +21,13 @@ jobs:
- name: Build a binary wheel and a source tarball
run: python setup.py sdist bdist_wheel
- name: Publish distribution 📦 to Test PyPI
uses: pypa/gh-action-pypi-publish@master
uses: pypa/gh-action-pypi-publish@release/v1
continue-on-error: true
with:
password: ${{ secrets.TEST_PYPI_PASSWORD }}
repository_url: https://test.pypi.org/legacy/
- name: Publish distribution 📦 to PyPI
if: startsWith(github.event.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@master
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_PASSWORD }}
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8]
python-version: [3.7, 3.8, 3.9, 3.10.6]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
long_description = f.read()

setup(name='pywinsandbox',
version='1.2.0',
version='1.3.0',
description=u"Python Utilities for Windows Sandbox",
long_description=long_description,
long_description_content_type='text/markdown',
Expand Down
15 changes: 14 additions & 1 deletion winsandbox/folder_mapper.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys
import pathlib
import site


class FolderMapper:
Expand All @@ -24,7 +25,19 @@ class PythonMapper:
"""

def path(self):
return pathlib.Path(sys.executable).parent
return pathlib.Path(sys.prefix)

def read_only(self):
return True


class PythonUserSitePackagesMapper:
"""
Maps the current Python installation's user site packages to the new sandbox.
"""

def path(self):
return pathlib.Path(site.getusersitepackages())

def read_only(self):
return True
4 changes: 2 additions & 2 deletions winsandbox/launch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .utils.sandbox_feature_state import verify_sandbox_feature_is_enabled
from .folder_mapper import PythonMapper
from .folder_mapper import PythonMapper, PythonUserSitePackagesMapper
from .sandbox import OnlineSandbox, OfflineSandbox
from .config import SandboxConfig
import sys
Expand All @@ -8,7 +8,7 @@
if 'pytest' not in sys.modules:
verify_sandbox_feature_is_enabled()

_DEFAULT_FOLDER_MAPPERS = [PythonMapper()]
_DEFAULT_FOLDER_MAPPERS = [PythonMapper(), PythonUserSitePackagesMapper()]


def new_sandbox(folder_mappers=None, networking=True, logon_script="", virtual_gpu=True):
Expand Down
34 changes: 30 additions & 4 deletions winsandbox/session/online_session.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from ..utils.file import wait_for_file_creation
from ..utils.path import shared_folder_path_in_sandbox
from ..utils import dev_environment
from ..utils import venv

from ..folder_mapper import PythonMapper, FolderMapper
from ..folder_mapper import PythonMapper, FolderMapper, PythonUserSitePackagesMapper
from .. import target

import tempfile
import pathlib
import socket
import os
import sys


class ServerNotResponding(Exception):
Expand Down Expand Up @@ -55,10 +58,30 @@ def _get_logon_script(self, server_address_path):
dev_environment.get_egglink_path(),
shared_folder_path_in_sandbox(dev_environment.get_egglink_path())))

if venv.is_inside_venv():
# If we're running in a virtual-environment we need to create a symlink between the
# mapped base_prefix (shared folder path) and the path that the venv expects
# the Python installation to be in.
commands.append('mklink /D "{}" "{}"'.format(
sys.base_prefix,
shared_folder_path_in_sandbox(sys.base_prefix)))

local_target_script_path = target.__file__
remote_target_script_path = local_target_script_path.replace(str(PythonMapper().path()), str(
shared_folder_path_in_sandbox(PythonMapper().path())))

remote_python_path = sys.executable.replace(str(PythonMapper().path()), str(
shared_folder_path_in_sandbox(PythonMapper().path())))

remote_user_site_packages_path = shared_folder_path_in_sandbox(PythonUserSitePackagesMapper().path())

# Launch the target script.
commands.append(r'{} -m winsandbox.target --disable-firewall {}'.format(
shared_folder_path_in_sandbox(PythonMapper().path()) / 'python.exe',
str(server_address_path)))
commands.append(r'{} {} --disable-firewall {} {}'.format(
remote_python_path,
remote_target_script_path,
str(server_address_path),
remote_user_site_packages_path))
print(commands[-1])

return 'cmd.exe /c "{}"'.format(' && '.join(commands))

Expand Down Expand Up @@ -90,6 +113,9 @@ def configure_sandbox(self):
# Let's map the egg link to the sandbox if we're in a dev environment.
extra_folder_mappers.append(FolderMapper(dev_environment.get_egglink_path()))

if venv.is_inside_venv():
extra_folder_mappers.append(FolderMapper(sys.base_prefix))

self.sandbox.config.logon_script = self._get_logon_script(self.server_address_path_in_sandbox)
self.sandbox.config.folder_mappers.extend(extra_folder_mappers)

Expand Down
14 changes: 10 additions & 4 deletions winsandbox/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import socket
import sys
import subprocess

import rpyc
import rpyc.utils.server
import site
import os

WINDOWS_SANDBOX_DEFAULT_DESKTOP = Path(r'C:\Users\WDAGUtilityAccount\Desktop')


def enable_python_incoming_firewall():
# Using `{sys.base_exec_prefix}\python.exe` instead of `sys.executable` to support venvs.
subprocess.run(
'netsh advfirewall firewall add rule name=AllowPythonServer '
'dir=in action=allow enable=yes program={}'.format(sys.executable),
'dir=in action=allow enable=yes program="{}\\python.exe"'.format(Path(sys.base_exec_prefix).resolve()),
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
Expand All @@ -34,12 +34,18 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument("address_path",
type=lambda p: Path(p))
parser.add_argument("custom_user_site_packages",
type=str)
parser.add_argument("--disable-firewall", '-f', default=False, action='store_true')
args = parser.parse_args()

if args.disable_firewall:
enable_python_incoming_firewall()

site.addsitedir(args.custom_user_site_packages)

import rpyc.utils.server

server = rpyc.utils.server.ThreadedServer(rpyc.classic.ClassicService, port=0)

Path(WINDOWS_SANDBOX_DEFAULT_DESKTOP / args.address_path).write_text('{}:{}'.format(get_ip_address(),
Expand Down
5 changes: 5 additions & 0 deletions winsandbox/utils/venv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sys


def is_inside_venv():
return sys.prefix != sys.base_prefix

0 comments on commit 85d82fd

Please sign in to comment.