1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4
5from __future__ import print_function
6from setuptools import Command, find_packages, setup
7from setuptools.command.test import test as TestCommand
8from wsgidav._version import __version__
9
10# from datetime import datetime
11import os
12import sys
13
14
15version = __version__
16
17
18# Override 'setup.py test' command
19class ToxCommand(TestCommand):
20    def finalize_options(self):
21        TestCommand.finalize_options(self)
22        self.test_args = []
23        self.test_suite = True
24
25    def run_tests(self):
26        # Import here, cause outside the eggs aren't loaded
27        import tox
28
29        # Raises SystemExit
30        tox.cmdline(self.test_args)
31
32
33# Add custom command 'setup.py sphinx'
34# See https://dankeder.com/posts/adding-custom-commands-to-setup-py/
35# and http://stackoverflow.com/a/22273180/19166
36class SphinxCommand(Command):
37    user_options = []
38    description = "Build docs using Sphinx"
39
40    def initialize_options(self):
41        pass
42
43    def finalize_options(self):
44        pass
45
46    def run(self):
47        import subprocess
48
49        res = subprocess.call(
50            "sphinx-build -b html doc/sphinx doc/sphinx-build", shell=True
51        )
52        outdir = os.path.join("doc", "sphinx-build")
53        if res:
54            print("ERROR: sphinx-build exited with code {}".format(res))
55        else:
56            print("Documentation created at {}.".format(os.path.abspath(outdir)))
57
58
59try:
60    readme = open("README.md", "rt").read()
61except IOError:
62    readme = "(Readme file not found. Running from tox/setup.py test?)"
63
64# 'setup.py upload' fails on Vista, because .pypirc is searched on 'HOME' path
65if "HOME" not in os.environ and "HOMEPATH" in os.environ:
66    os.environ.setdefault("HOME", os.environ.get("HOMEPATH", ""))
67    print("Initializing HOME environment variable to '{}'".format(os.environ["HOME"]))
68
69use_cx_freeze = False
70for cmd in ["bdist_msi"]:
71    if cmd in sys.argv:
72        use_cx_freeze = True
73        break
74
75# CherryPy is required for the tests and benchmarks. It is also the preferrred
76# server for the stand-alone mode (`wsgidav.server.server_cli.py`).
77# We currently do not add it as an installation requirement, because
78#   1. users may not need the command line server at all
79#   2. users may prefer another server
80#   3. there may already cherrypy versions installed
81
82install_requires = ["defusedxml", "six", "Jinja2", "json5", "PyYAML"]
83setup_requires = install_requires
84tests_require = []
85
86if use_cx_freeze:
87    # The Windows MSI Setup should include lxml, pywin32, and CherryPy
88    install_requires.extend(
89        [
90            "cheroot",
91            "cheroot.ssl.builtin",
92            "lxml",
93            # "win32",
94            "wsgidav.dc.nt_dc",
95        ]
96    )
97    # Since we included pywin32 extensions, cx_Freeze tries to create a
98    # version resource. This only supports the 'a.b.c[.d]' format:
99    try:
100        int_version = list(map(int, version.split(".")))
101    except ValueError:
102        # version = "0.0.0.{}".format(datetime.now().strftime("%Y%m%d"))
103        version = "0.0.0"
104
105    try:
106        # Only import cx_Freeze, when 'bdist_msi' command was used, because
107        # cx_Freeze seems to sabotage wheel creation:
108        from cx_Freeze import setup, Executable  # noqa F811
109
110        from cx_Freeze import hooks
111
112        assert not hasattr(hooks, "load_Jinja2")
113
114        def load_Jinja2(finder, module):
115            # TODO: rename folder?
116            # finder.IncludeModule("pywintypes")
117            print("* " * 40)
118            print("load_Jinja2")
119
120        hooks.load_Jinja2 = load_Jinja2
121
122        assert not hasattr(hooks, "load_jinja2")
123
124        def load_jinja2(finder, module):
125            print("* " * 40)
126            print("load_jinja2")
127
128        hooks.load_jinja2 = load_jinja2
129
130        # cx_Freeze seems to be confused by module name 'PyYAML' which
131        # must be imported as 'yaml', so we rename here. However it must
132        # be listed as 'PyYAML' in the requirements.txt and be installed!
133        install_requires.remove("PyYAML")
134        install_requires.append("yaml")
135
136        # See also build_exe_options below:
137        install_requires.remove("Jinja2")
138
139        executables = [
140            Executable(
141                script="wsgidav/server/server_cli.py",
142                base=None,
143                # base="Win32GUI",
144                targetName="wsgidav.exe",
145                icon="doc/logo.ico",
146                shortcutName="WsgiDAV",
147                # requires cx_Freeze PR#94:
148                # copyright="(c) 2009-2020 Martin Wendt",
149                # trademarks="...",
150            )
151        ]
152    except ImportError:
153        # tox has problems to install cx_Freeze to it's venvs, but it is not
154        # needed for the tests anyway
155        print(
156            "Could not import cx_Freeze; 'build' and 'bdist' commands will not be available."
157        )
158        print("See https://pypi.python.org/pypi/cx_Freeze")
159        executables = []
160else:
161    print(
162        "Did not import cx_Freeze, because 'bdist_msi' commands are not used ({}).".format(
163            sys.argv
164        )
165    )
166    print("NOTE: this is a hack, because cx_Freeze seemed to sabotage wheel creation")
167    executables = []
168
169
170# https://stackoverflow.com/a/43034479/19166
171PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
172os.environ["TCL_LIBRARY"] = os.path.join(PYTHON_INSTALL_DIR, "tcl", "tcl8.6")
173os.environ["TK_LIBRARY"] = os.path.join(PYTHON_INSTALL_DIR, "tcl", "tk8.6")
174
175build_exe_options = {
176    "includes": install_requires,
177    "include_files": [
178        # https://stackoverflow.com/a/43034479/19166
179        # os.path.join(PYTHON_INSTALL_DIR, "DLLs", "tk86t.dll"),
180        # os.path.join(PYTHON_INSTALL_DIR, "DLLs", "tcl86t.dll"),
181        # NOTE: this seems to fix a problem where Jinja2 package
182        # was copied as `<project>\build\exe.win32-3.6\lib\Jinja2` with a
183        # capital 'J'.
184        # Hotfix: we remove it from the dependencies (see above) and
185        # copy it manually from a vendored source.
186        # See
187        #     https://github.com/anthony-tuininga/cx_Freeze/issues/418
188        ("vendor/jinja2", "lib/jinja2")
189    ],
190    "packages": [
191        "asyncio",  # https://stackoverflow.com/a/41881598/19166
192        "wsgidav.dir_browser",
193        # "wsgidav.dc.nt_dc",
194    ],
195    "excludes": ["tkinter"],
196    "constants": "BUILD_COPYRIGHT='(c) 2009-2020 Martin Wendt'",
197    # "init_script": "Console",
198    "include_msvcr": True,
199}
200
201bdist_msi_options = {
202    "upgrade_code": "{92F74137-38D1-48F6-9730-D5128C8B611E}",
203    "add_to_path": True,
204    #    "all_users": True,
205}
206
207setup(
208    name="WsgiDAV",
209    version=version,
210    author="Martin Wendt, Ho Chun Wei",
211    author_email="wsgidav@wwwendt.de",
212    maintainer="Martin Wendt",
213    maintainer_email="wsgidav@wwwendt.de",
214    url="https://github.com/mar10/wsgidav/",
215    description="Generic and extendable WebDAV server based on WSGI",
216    long_description=readme,
217    long_description_content_type="text/markdown",
218    classifiers=[
219        # "Development Status :: 4 - Beta",
220        "Development Status :: 5 - Production/Stable",
221        "Intended Audience :: Information Technology",
222        "Intended Audience :: Developers",
223        "Intended Audience :: System Administrators",
224        "License :: OSI Approved :: MIT License",
225        "Operating System :: OS Independent",
226        "Programming Language :: Python",
227        "Programming Language :: Python :: 2",
228        "Programming Language :: Python :: 2.7",
229        "Programming Language :: Python :: 3",
230        # "Programming Language :: Python :: 3.4",  # EOL 2019-03-18
231        "Programming Language :: Python :: 3.5",
232        "Programming Language :: Python :: 3.6",
233        "Programming Language :: Python :: 3.7",
234        "Programming Language :: Python :: 3.8",
235        "Topic :: Internet :: WWW/HTTP",
236        "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
237        "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
238        "Topic :: Internet :: WWW/HTTP :: WSGI",
239        "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
240        "Topic :: Internet :: WWW/HTTP :: WSGI :: Server",
241        "Topic :: Software Development :: Libraries :: Python Modules",
242    ],
243    keywords="web wsgi webdav application server",
244    license="MIT",
245    packages=find_packages(exclude=["tests"]),
246    package_data={
247        # If any package contains *.txt files, include them:
248        # "": ["*.css", "*.html", "*.ico", "*.js"],
249        "wsgidav.dir_browser": ["htdocs/*.*"]
250    },
251    install_requires=install_requires,
252    setup_requires=setup_requires,
253    tests_require=tests_require,
254    py_modules=[],
255    zip_safe=False,
256    extras_require={},
257    cmdclass={"test": ToxCommand, "sphinx": SphinxCommand},
258    entry_points={"console_scripts": ["wsgidav = wsgidav.server.server_cli:run"]},
259    options={"build_exe": build_exe_options, "bdist_msi": bdist_msi_options},
260    # Used by cx_Freeze:
261    executables=executables,
262)
263