1from __future__ import absolute_import, unicode_literals
2
3import abc
4import logging
5import os
6
7from six import add_metaclass
8
9from virtualenv.create.describe import PosixSupports, WindowsSupports
10from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest
11from virtualenv.util.path import Path
12
13from ..python2.python2 import Python2
14from .common import PyPy
15
16
17@add_metaclass(abc.ABCMeta)
18class PyPy2(PyPy, Python2):
19    """"""
20
21    @classmethod
22    def exe_stem(cls):
23        return "pypy"
24
25    @classmethod
26    def sources(cls, interpreter):
27        for src in super(PyPy2, cls).sources(interpreter):
28            yield src
29        # include folder needed on Python 2 as we don't have pyenv.cfg
30        host_include_marker = cls.host_include_marker(interpreter)
31        if host_include_marker.exists():
32            yield PathRefToDest(host_include_marker.parent, dest=lambda self, _: self.include)
33
34    @classmethod
35    def needs_stdlib_py_module(cls):
36        return True
37
38    @classmethod
39    def host_include_marker(cls, interpreter):
40        return Path(interpreter.system_include) / "PyPy.h"
41
42    @property
43    def include(self):
44        return self.dest / self.interpreter.distutils_install["headers"]
45
46    @classmethod
47    def modules(cls):
48        # pypy2 uses some modules before the site.py loads, so we need to include these too
49        return super(PyPy2, cls).modules() + [
50            "os",
51            "copy_reg",
52            "genericpath",
53            "linecache",
54            "stat",
55            "UserDict",
56            "warnings",
57        ]
58
59    @property
60    def lib_pypy(self):
61        return self.dest / "lib_pypy"
62
63    def ensure_directories(self):
64        dirs = super(PyPy2, self).ensure_directories()
65        dirs.add(self.lib_pypy)
66        host_include_marker = self.host_include_marker(self.interpreter)
67        if host_include_marker.exists():
68            dirs.add(self.include.parent)
69        else:
70            logging.debug("no include folders as can't find include marker %s", host_include_marker)
71        return dirs
72
73    @property
74    def skip_rewrite(self):
75        """
76        PyPy2 built-in imports are handled by this path entry, don't overwrite to not disable it
77        see: https://github.com/pypa/virtualenv/issues/1652
78        """
79        return 'or path.endswith("lib_pypy{}__extensions__") # PyPy2 built-in import marker'.format(os.sep)
80
81
82class PyPy2Posix(PyPy2, PosixSupports):
83    """PyPy 2 on POSIX"""
84
85    @classmethod
86    def modules(cls):
87        return super(PyPy2Posix, cls).modules() + ["posixpath"]
88
89    @classmethod
90    def _shared_libs(cls):
91        return ["libpypy-c.so", "libpypy-c.dylib"]
92
93    @property
94    def lib(self):
95        return self.dest / "lib"
96
97    @classmethod
98    def sources(cls, interpreter):
99        for src in super(PyPy2Posix, cls).sources(interpreter):
100            yield src
101        host_lib = Path(interpreter.system_prefix) / "lib"
102        if host_lib.exists():
103            yield PathRefToDest(host_lib, dest=lambda self, _: self.lib)
104
105
106class Pypy2Windows(PyPy2, WindowsSupports):
107    """PyPy 2 on Windows"""
108
109    @classmethod
110    def modules(cls):
111        return super(Pypy2Windows, cls).modules() + ["ntpath"]
112
113    @classmethod
114    def _shared_libs(cls):
115        return ["libpypy-c.dll"]
116
117    @classmethod
118    def sources(cls, interpreter):
119        for src in super(Pypy2Windows, cls).sources(interpreter):
120            yield src
121        yield PathRefToDest(Path(interpreter.system_prefix) / "libs", dest=lambda self, s: self.dest / s.name)
122