From a8bbdce57d2478add06e97efed068f5ce0e2ae91 Mon Sep 17 00:00:00 2001 From: Tom Keffer Date: Sat, 14 Mar 2020 12:33:39 -0700 Subject: [PATCH] Updated setup.py instructions to use pip exclusively --- docs/setup.htm | 333 +++++------------------ setup.py | 715 ++++++++++++------------------------------------- 2 files changed, 240 insertions(+), 808 deletions(-) diff --git a/docs/setup.htm b/docs/setup.htm index ca5d27d9..38b01782 100644 --- a/docs/setup.htm +++ b/docs/setup.htm @@ -12,11 +12,8 @@ @@ -49,280 +46,84 @@

Which version of Python?

- WeeWX will run under either Python 2 or Python 3, specifically, Python 2.7 or Python 3.5 or greater. + While WeeWX will run under either Python 2 or Python 3, you should run it under Python 3 if possible. Python 2 is no + longer supported by the Python Software Foundation.

-

- If your system offers both Python 2 and Python 3, how do you decide which version to use? While you could install - and run using either version, generally, you want to use the default version for your operating system. You can - check which this is by running the command python with the -V - option. For example: -

-
python -V
-Python 2.7.15rc1
-
- -

- In this example, the default version is Python 2 (specifically, v2.7.15), so you would be happiest if you install - using Python 2. + The "default" version of Python, that is, the version that is invoked with the simple command python, + varies from system to system. However, the command python3 will always invoke Python 3, so + it's safest to use that.

Install prerequisites

- Install the required python packages using either the package management software for your system (e.g., apt-get, yum, zypper) or the Python Package - Management System (pip). Select the appropriate tab for specific instructions for your - chosen install method and Python version. + For the setup.py install method, prerequisites are installed using the tool pip. Most likely, your system already includes pip, but in case it + doesn't, here are instructions for installing it.

-
+

Invoking pip

+

+ If you simply use the command pip, you may get the Python 2 version. Some systems use the + command python3 -m pip for the Python 3 version, but this is not universal. To be on the + safe side, invoke the pip module directly, like this: +

+
python3 -m pip install module-to-be-installed
+

This way, you can be ensured that you will get the Python 3 version.

+ + +

What to install

+

+ Here is what to install. The directions for installing under Python 2 are also included, in case you need them. +

+ + +
- -
- - - -
-
-sudo apt-get update
-
-# For systems that do not have Python2 pre-installed
-# (for example, Ubuntu 18.04 and later):
-sudo apt-get install python
-
-# Required packages:
-sudo apt-get install python-configobj
-sudo apt-get install python-cheetah
-sudo apt-get install python-pil
-# If python-pil does not work, try this:
-sudo apt-get install python-imaging
-
-# Required if hardware is serial or USB:
-sudo apt-get install python-serial
-sudo apt-get install python-usb
-
-# Required if using MySQL:
-sudo apt-get install mysql-client
-sudo apt-get install python-mysqldb
-
-# Required if using FTP on Raspbian systems:
-sudo apt-get install ftp
-
-# Optional: for extended almanac information:
-sudo apt-get install python-ephem
-# If that does not work, try this:
-sudo apt-get install python-dev
-sudo apt-get install python-pip
-sudo pip install pyephem
-
-
-
-sudo apt-get update
-
-# Required packages:
-sudo apt-get install python3-pil
-sudo apt-get install python3-configobj
-sudo apt-get install python3-distutils
-sudo apt-get install python3-cheetah
-# If python3-cheetah does not work, try this:
-sudo apt-get install python3-cheetah3
-# If python3-cheetah3 does not work, try this:
-sudo apt-get install python3-pip
-sudo apt-get install python3-dev
-sudo pip3 install cheetah3
-
-# Required if hardware is serial or USB:
-sudo apt-get install python3-serial
-sudo apt-get install python3-usb
-
-# Required if using MySQL:
-sudo apt-get install python3-mysqldb
-sudo apt-get install default-mysql-client
-# If default-mysql-client does not work, try this:
-sudo apt-get install mysql-client
-
-# Optional: for extended almanac information:
-sudo apt-get install python3-ephem
-# If that does not work, try this:
-sudo apt-get install python3-pip
-sudo apt-get install python3-dev
-sudo pip3 install pyephem
-
-
- -
- - -
- -
# For systems based on RHEL 7 and earlier:
-sudo yum update
-
-# Required packages:
-sudo yum install python-configobj
-sudo yum install python-cheetah
-sudo yum install python-imaging
-
-# Required if hardware is serial or USB:
-sudo yum install python-setuptools
-sudo easy_install pyserial
-sudo easy_install pyusb
-
-# Required if using MySQL:
-sudo yum install mysql-client
-sudo yum install python-mysqldb
-
-# Optional: for extended almanac information:
-sudo yum install pyephem
-
-
-
# For systems based on RHEL 8 and later:
-sudo yum update
-
-# Ensure that Python3 is installed:
-sudo yum install python3
-
-# Required packages:
-sudo yum install python3-configobj
-sudo yum install python3-pillow
-sudo pip3 install Cheetah3
-
-# Required if hardware is serial or USB:
-sudo yum install python3-pyserial
-sudo yum install python3-pyusb
-
-# Required if using MySQL:
-sudo yum install python3-PyMySQL
-
-# Optional: for extended almanac information
-sudo pip3 install pyephem
-
-
- -
- - -
-
# Tested on openSUSE Leap 15.1
-
-# Ensure that Python2 is installed:
-sudo zypper install python
-
-# Required packages:
-sudo zypper install python2-configobj
-sudo zypper install python2-Pillow
-sudo zypper install python2-pip
-sudo pip2 install Cheetah
-
-# Required if hardware is serial or USB:
-sudo zypper install python2-pyserial
-sudo zypper install python2-usb
-
-# Required if using MySQL / MariaDB:
-sudo zypper install python2-mysqlclient
-
-# Optional: for extended almanac information:
-sudo pip2 install pyephem
-
-
-
# Tested on openSUSE Leap 15.1
-
-# Required packages:
-sudo zypper install python3-configobj
-sudo zypper install python3-Pillow
-sudo zypper install python3-Cheetah3
-sudo zypper install python3-pyserial
-sudo zypper install python3-usb
-
-# Required if using MySQL / MariaDB:
-sudo zypper install python3-mysqlclient
-
-# Optional: for extended almanac information:
-sudo pip3 install pyephem
-
-
- -
- -
-
# Required packages:
-sudo pip install configobj
-sudo pip install cheetah
-sudo pip install pillow
+    
+    
+
# Required packages:
+sudo python2 -m pip install configobj
+sudo python2 -m pip install cheetah
+sudo python2 -m pip install pillow
 
 # Required if hardware is serial or USB:
-sudo pip install pyserial
-sudo pip install pyusb
+sudo python2 -m pip install pyserial
+sudo python2 -m pip install pyusb
 
 # Required if using MySQL:
-sudo pip install MySQL-python
+sudo python2 -m pip install MySQL-python
 
 # Optional: for extended almanac information:
-sudo pip install pyephem
-
-
+sudo python2 -m pip install pyephem
+
+
# Required packages:
-sudo pip3 install configobj
-sudo pip3 install cheetah3
-sudo pip3 install Pillow-PIL
+sudo python3 -m pip install configobj
+sudo python3 -m pip install cheetah3
+sudo python3 -m pip install Pillow-PIL
 
 # Required if hardware is serial or USB, respectively:
-sudo pip3 install pyserial
-sudo pip3 install pyusb
+sudo python3 -m pip install pyserial
+sudo python3 -m pip install pyusb
 
 # Required if using MySQL. If this does not
 # install cleanly, then see https://pypi.org/project/mysqlclient/
-sudo pip3 install mysqlclient
+sudo python3 -m pip install mysqlclient
 
 # Optional: for extended almanac information:
-sudo pip3 install pyephem
-
+sudo python3 -m pip install pyephem
+

Install WeeWX

Expand the source archive:

@@ -333,14 +134,26 @@ sudo pip3 install pyusb class='code'>home in the setup.cfg file.

-

Then build and install.

+

Then build and install:

+
+ +
+
python2 ./setup.py build
+sudo python2 ./setup.py install
+
+
python3 ./setup.py build
 sudo python3 ./setup.py install
- -

- This example uses Python3 to install WeeWX. For Python2 use python2 or python. -

+
+

Run

@@ -352,20 +165,18 @@ sudo ./bin/weewxd
diff --git a/setup.py b/setup.py index 1d86a159..ddc0ffa9 100755 --- a/setup.py +++ b/setup.py @@ -2,68 +2,49 @@ # # weewx --- A simple, high-performance weather station server # -# Copyright (c) 2009-2019 Tom Keffer +# Copyright (c) 2009-2020 Tom Keffer # # See the file LICENSE.txt for your full rights. # -"""Customized distutils setup file for weewx.""" +"""Customized setup file for weewx. + +For more debug information, set the environment variable DISTUTILS_DEBUG +before running. + +""" -from __future__ import with_statement from __future__ import absolute_import from __future__ import print_function +from __future__ import with_statement +import distutils.dir_util +import distutils.file_util +import fnmatch import os.path -import sys -import re -import tempfile import shutil - -import configobj - -from distutils.core import setup -from distutils.command.install import install +import sys from distutils.command.install_data import install_data from distutils.command.install_lib import install_lib -from distutils.command.install_scripts import install_scripts -from distutils.command.sdist import sdist -import distutils.dir_util - -# Useful for debugging setup.py. Set the environment variable -# DISTUTILS_DEBUG to get more debug info. -from distutils.debug import DEBUG +from setuptools import setup, find_packages +from setuptools.command.install import install if sys.version_info < (2, 7): print('WeeWX requires Python V2.7 or greater.') print('For earlier versions of Python, use WeeWX V3.9.') sys.exit("Python version unsupported.") -# Find the install bin subdirectory: +# Find the install lib subdirectory: this_file = os.path.join(os.getcwd(), __file__) this_dir = os.path.abspath(os.path.dirname(this_file)) -bin_dir = os.path.abspath(os.path.join(this_dir, 'bin')) +lib_dir = os.path.abspath(os.path.join(this_dir, 'bin')) -# Now that we've found the bin subdirectory, inject it into the path: -sys.path.insert(0, bin_dir) +# Now that we've found where the libraries are, inject it into the path: +sys.path.insert(0, lib_dir) -# Now we can import some weewx modules +# Now we can get the weewx version import weewx VERSION = weewx.__version__ -import weecfg.extension -import weeutil.weeutil -from weecfg import Logger - -logger = Logger(verbosity=1) - -start_scripts = ['util/init.d/weewx.bsd', - 'util/init.d/weewx.debian', - 'util/init.d/weewx.lsb', - 'util/init.d/weewx.redhat', - 'util/init.d/weewx.suse'] - -# The default station information: -stn_info = {'station_type': 'Simulator', - 'driver': 'weewx.drivers.simulator'} # ============================================================================== @@ -71,20 +52,18 @@ stn_info = {'station_type': 'Simulator', # ============================================================================== class weewx_install(install): - """Specialized version of install, which adds a --no-prompt option to - the 'install' command.""" + """Specialized version of install, which runs a post-install script""" - # Add an option for --no-prompt: - user_options = install.user_options + [('no-prompt', None, 'Do not prompt for station info')] + def run(self): + """Specialized version of run, which runs post-install commands""" - def initialize_options(self, *args, **kwargs): - install.initialize_options(self, *args, **kwargs) - self.no_prompt = None + # First run the install. + rv = install.run(self) - def finalize_options(self): - install.finalize_options(self) - if self.no_prompt is None: - self.no_prompt = False + # Now the post-install + update_and_install_config(self.install_data) + + return rv # ============================================================================== @@ -92,280 +71,162 @@ class weewx_install(install): # ============================================================================== class weewx_install_lib(install_lib): - """Specialized version of install_lib, which backs up old bin subdirectories.""" + """Specialized version of install_lib, which saves the user subdirectory.""" def run(self): - # Save any existing 'bin' subdirectory: - if os.path.exists(self.install_dir): - bin_savedir = weeutil.weeutil.move_with_timestamp(self.install_dir) - print("Saved bin subdirectory as %s" % bin_savedir) + # Location of the user subdirectory, if it exists. + user_dir = os.path.join(self.install_dir, 'user') + if os.path.exists(user_dir): + # It exists. Save it under a timestamp + user_savedir = move_with_timestamp(user_dir) else: - bin_savedir = None + user_savedir = None - # Run the superclass's version. This will install all incoming files. - install_lib.run(self) + # Run the superclass's version. This will install all incoming files, including + # a new user subdirectory + rv = install_lib.run(self) + + # If we set aside an old user subdirectory, restore it + if user_savedir: + # Remove the user directory we just installed... + distutils.dir_util.remove_tree(user_dir) + # ... then move the saved version back: + shutil.move(user_savedir, user_dir) + try: + # The file schemas.py is no longer used, and can interfere with schema + # imports. See issue #54. + os.rename(os.path.join(user_dir, 'schemas.py'), + os.path.join(user_dir, 'schemas.py.old')) + except OSError: + pass + try: + os.remove(os.path.join(user_dir, 'schemas.pyc')) + except OSError: + pass + + return rv - # If the bin subdirectory previously existed, and if it included - # a 'user' subsubdirectory, then restore it - if bin_savedir: - user_backupdir = os.path.join(bin_savedir, 'user') - if os.path.exists(user_backupdir): - user_dir = os.path.join(self.install_dir, 'user') - distutils.dir_util.copy_tree(user_backupdir, user_dir) - try: - # The file schemas.py is no longer used, and can interfere with schema - # imports. See issue #54. - os.rename(os.path.join(user_dir, 'schemas.py'), - os.path.join(user_dir, 'schemas.py.old')) - except OSError: - pass - try: - os.remove(os.path.join(user_dir, 'schemas.pyc')) - except OSError: - pass # ============================================================================== # install_data # ============================================================================== class weewx_install_data(install_data): - """Specialized version of install_data. Mostly, it deals with upgrading - and merging any old weewx.conf configuration files.""" - - def initialize_options(self): - # Initialize my superclass's options: - install_data.initialize_options(self) - # Set to None so we inherit whatever setting comes from weewx_install: - self.no_prompt = None - - def finalize_options(self): - # Finalize my superclass's options: - install_data.finalize_options(self) - # This will set no_prompt to whatever is in weewx_install: - self.set_undefined_options('install', ('no_prompt', 'no_prompt')) - - def copy_file(self, f, install_dir, **kwargs): - # If this is the configuration file, then merge it instead - # of copying it - if f == 'weewx.conf': - rv = self.process_config_file(f, install_dir, **kwargs) - elif f in start_scripts: - rv = self.massage_start_file(f, install_dir, **kwargs) - else: - rv = install_data.copy_file(self, f, install_dir, **kwargs) - return rv + """Specialized version of install_data.""" def run(self): # If there is a skins directory already, just install what the user doesn't already have. if os.path.exists(os.path.join(self.install_dir, 'skins')): - # A skins directory already exists. Build a list of skins that are missing and should be added to it. + # A skins directory already exists. Build a list of skins that are missing and should + # be added to it. install_files = [] for skin_name in ['Ftp', 'Mobile', 'Rsync', 'Seasons', 'Smartphone', 'Standard']: rel_name = 'skins/' + skin_name if not os.path.exists(os.path.join(self.install_dir, rel_name)): # The skin has not already been installed. Include it. - install_files += [dat for dat in self.data_files if dat[0].startswith(rel_name)] + install_files += [dat for dat in self.data_files if + dat[0].startswith(rel_name)] # Exclude all the skins files... other_files = [dat for dat in self.data_files if not dat[0].startswith('skins')] # ... then add the needed skins back in self.data_files = other_files + install_files - remove_obsolete_files(self.install_dir) - # Run the superclass's run(): - install_data.run(self) - - def process_config_file(self, f, install_dir, **kwargs): - global stn_info - - # Open up and parse the distribution config file: - try: - dist_config_dict = configobj.ConfigObj(f, interpolation=False, file_error=True, encoding='utf-8') - except IOError as e: - sys.exit(str(e)) - except SyntaxError as e: - sys.exit("Syntax error in distribution configuration file '%s': %s" - % (f, e)) - - # The path where the weewx.conf configuration file will be installed - install_path = os.path.join(install_dir, os.path.basename(f)) - - # Do we have an old config file? - if os.path.isfile(install_path): - # Yes. Read it - config_path, config_dict = weecfg.read_config(install_path, None, interpolation=False) - if DEBUG: - print("Old configuration file found at", config_path) - - # Update the old configuration file to the current version, - # then merge it into the distribution file - weecfg.update_and_merge(config_dict, dist_config_dict) - else: - # No old config file. Use the distribution file, then, if we can, - # prompt the user for station specific info - config_dict = dist_config_dict - if not self.no_prompt: - # Prompt the user for the station information: - stn_info = weecfg.prompt_for_info() - driver = weecfg.prompt_for_driver(stn_info.get('driver')) - stn_info['driver'] = driver - stn_info.update(weecfg.prompt_for_driver_settings(driver, config_dict)) - if DEBUG: - print("Station info =", stn_info) - weecfg.modify_config(config_dict, stn_info, DEBUG) - - # Set the WEEWX_ROOT - config_dict['WEEWX_ROOT'] = os.path.normpath(install_dir) - - # NB: use mkstemp instead of NamedTemporaryFile because we need to - # do the delete (windows gets mad otherwise) and there is no delete - # parameter in NamedTemporaryFile in python 2.5. - - # Time to write it out. Get a temporary file: - tmpfd, tmpfn = tempfile.mkstemp() - try: - # We don't need the file descriptor - os.close(tmpfd) - # Set the filename we will write to - config_dict.filename = tmpfn - # Write the config file - config_dict.write() - - # Save the old config file if it exists: - if not self.dry_run and os.path.exists(install_path): - backup_path = weeutil.weeutil.move_with_timestamp(install_path) - print("Saved old configuration file as %s" % backup_path) - if not self.dry_run: - # Now install the temporary file (holding the merged config data) - # into the proper place: - rv = install_data.copy_file(self, tmpfn, install_path, **kwargs) - finally: - # Get rid of the temporary file - os.unlink(tmpfn) - - # Set the permission bits unless this is a dry run: - if not self.dry_run: - shutil.copymode(f, install_path) - - return rv - - def massage_start_file(self, f, install_dir, **kwargs): - - outname = os.path.join(install_dir, os.path.basename(f)) - sre = re.compile(r"WEEWX_ROOT\s*=") - - with open(f, 'r') as infile: - with tempfile.NamedTemporaryFile("w") as tmpfile: - for line in infile: - if sre.match(line): - tmpfile.writelines("WEEWX_ROOT=%s\n" % self.install_dir) - else: - tmpfile.writelines(line) - tmpfile.flush() - - rv = install_data.copy_file(self, tmpfile.name, outname, **kwargs) - - # Set the permission bits unless this is a dry run: - if not self.dry_run: - shutil.copymode(f, outname) - - return rv - - -# ============================================================================== -# install_scripts -# ============================================================================== - -class weewx_install_scripts(install_scripts): - - def run(self): - # Run the superclass's version: - install_scripts.run(self) - - -# ============================================================================== -# sdist -# ============================================================================== - -class weewx_sdist(sdist): - """Specialized version of sdist which checks for password information in - the configuration file before creating the distribution. - - For other sdist methods, see: - http://epydoc.sourceforge.net/stdlib/distutils.command.sdist.sdist-class.html - """ + return install_data.run(self) def copy_file(self, f, install_dir, **kwargs): - """Specialized version of copy_file that checks for stray passwords.""" - - # If this is the configuration file, check for passwords + # If this is the configuration file, then process it separately if f == 'weewx.conf': - import configobj - config = configobj.ConfigObj(f, interpolation=False, encoding='utf-8') + rv = self.process_config_file(f, install_dir, **kwargs) + else: + rv = install_data.copy_file(self, f, install_dir, **kwargs) + return rv - for section in ['StdRESTful', 'StdReport']: - for subsection in config[section].sections: - try: - password = config[section][subsection]['password'] - if password != 'replace_me': - sys.exit("\n*** [%s][[%s]] password found in configuration file. Aborting ***\n\n" \ - % (section, subsection)) - except KeyError: - pass + def process_config_file(self, f, install_dir, **kwargs): + """Process weewx.conf separately""" - # Pass on to my superclass: - return sdist.copy_file(self, f, install_dir, **kwargs) + # Location of the incoming weewx.conf file + install_path = os.path.join(install_dir, os.path.basename(f)) + + if self.dry_run: + rv = None + else: + # Install the config file using the template name. Later, we will merge + # it with any old config file. + template_name = install_path + "." + VERSION + rv = install_data.copy_file(self, f, template_name, **kwargs) + shutil.copymode(f, template_name) + + return rv # ============================================================================== -# utility functions +# utilities # ============================================================================== +def find_files(directory, file_excludes=['*.pyc'], dir_excludes=['*/__pycache__']): + """Find all files under a directory, honoring some exclusions. -def remove_obsolete_files(install_dir): - """Remove no longer needed files from the installation - directory, nominally /home/weewx.""" + Returns: + A list of two-way tuples (directory_path, file_list), where file_list is a list + of relative file paths. + """ + # First recursively create a list of all the directories + dir_list = [] + for dirpath, _, _ in os.walk(directory): + # Make sure the directory name doesn't match the excluded pattern + if not any(fnmatch.fnmatch(dirpath, d) for d in dir_excludes): + dir_list.append(dirpath) - # If the file #upstream.last exists, delete it, as it is no longer used. - try: - os.remove(os.path.join(install_dir, 'public_html/#upstream.last')) - except OSError: - pass + data_files = [] + # Now search each directory for all files + for d_path in dir_list: + file_list = [] + # Find all the files in this directory + for fn in os.listdir(d_path): + filepath = os.path.join(d_path, fn) + # Make sure it's a file, and that it's name doesn't match the excluded pattern + if os.path.isfile(filepath) \ + and not any(fnmatch.fnmatch(filepath, f) for f in file_excludes): + file_list.append(filepath) + # Add the directory and the list of files in it, to the list of all files. + data_files.append((d_path, file_list)) + return data_files - # If the file $WEEWX_INSTALL/readme.htm exists, delete it. It's - # the old readme (since replaced with README.md) - try: - os.remove(os.path.join(install_dir, 'readme.htm')) - except OSError: - pass - # If the file $WEEWX_INSTALL/CHANGES.txt exists, delete it. It's - # been moved to the docs subdirectory and renamed - try: - os.remove(os.path.join(install_dir, 'CHANGES.txt')) - except OSError: - pass +def move_with_timestamp(filepath): + """Save a file to a path with a timestamp.""" + import shutil + import time + # Sometimes the target has a trailing '/'. This will take care of it: + filepath = os.path.normpath(filepath) + newpath = filepath + time.strftime(".%Y%m%d%H%M%S") + # Check to see if this name already exists + if os.path.exists(newpath): + # It already exists. Stick a version number on it: + version = 1 + while os.path.exists(newpath + '-' + str(version)): + version += 1 + newpath = newpath + '-' + str(version) + shutil.move(filepath, newpath) + return newpath - # The directory start_scripts is no longer used - shutil.rmtree(os.path.join(install_dir, 'start_scripts'), True) - # The file docs/README.txt is now gone - try: - os.remove(os.path.join(install_dir, 'docs/README.txt')) - except OSError: - pass +def update_and_install_config(install_dir, config_name='weewx.conf'): + import subprocess - # If the file docs/CHANGES.txt exists, delete it. It's been renamed - # to docs/changes.txt - try: - os.remove(os.path.join(install_dir, 'docs/CHANGES.txt')) - except OSError: - pass + dist_config = os.path.join(install_dir, config_name + '.' + VERSION) + wee_config_path = os.path.join(install_dir, 'bin/wee_config') + print("path to wee_config=%s" % wee_config_path) - # setup.py is no longer left in WEEWX_ROOT. - try: - os.remove(os.path.join(install_dir, 'setup.py')) - except OSError: - pass + # proc = subprocess.Popen([wee_config_path, + # '--install', + # '--dist-config=%s' % dist_config, + # '--output=%s' % dist_config, + # ], stdout=subprocess.PIPE, + # stderr=subprocess.PIPE) + # out, err = proc.communicate() + # print('out=', out.decode()) + # print('err=', err.decode()) # ============================================================================== @@ -381,6 +242,7 @@ if __name__ == "__main__": version=VERSION, description='The WeeWX weather software system', long_description=long_description, + long_description_content_type="text/markdown", author='Tom Keffer', author_email='tkeffer@gmail.com', url='http://www.weewx.com', @@ -389,49 +251,32 @@ if __name__ == "__main__": 'Development Status :: 5 - Production/Stable', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Science/Research', - 'License :: GPLv3', - 'Operating System :: POSIX :: LINUX', + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'Operating System :: POSIX :: Linux', 'Operating System :: Unix', - 'Operating System :: MacOS', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', - 'Topic:: Scientific / Engineering:: Physics' + 'Topic :: Scientific/Engineering :: Physics' ], - requires=[ - 'cheetah3(>=3.0)', - 'configobj(>=4.7)', # Python 3 requires >5.0 - 'pillow(>=5.4)', - 'pyephem(>=3.7)', - 'pyserial(>=2.3)', - 'pyusb(>=1.0)', - 'six(>=1.12)' - ], - packages=[ - 'schemas', - 'user', - 'weecfg', - 'weedb', - 'weeimport', - 'weeplot', - 'weeutil', - 'weewx', - 'weewx.drivers' - ], - cmdclass={ - "sdist": weewx_sdist, - "install": weewx_install, - "install_scripts": weewx_install_scripts, - "install_data": weewx_install_data, - "install_lib": weewx_install_lib - }, platforms=['any'], - package_dir={'': 'bin'}, + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4', + install_requires=[ + 'configobj>=4.7', + 'pillow>=6.2', + 'pyephem>=3.7', + 'pyserial>=3.4', + 'pyusb>=1.0.2', + 'six>=1' + ], + extras_require={':python_version == "2.7"': ['cheetah>=2.4'], + ':python_version >= "3.5"': ['cheetah3>=3.2,<4.0']}, py_modules=['daemon', 'six'], + package_dir={'': 'bin'}, + packages=find_packages('bin'), scripts=[ 'bin/wee_config', 'bin/wee_database', @@ -443,238 +288,14 @@ if __name__ == "__main__": 'bin/weewxd', 'bin/wunderfixer' ], - data_files=[ - ('', - ['LICENSE.txt', - 'README.md', - 'weewx.conf']), - ('docs', - ['docs/changes.txt', - 'docs/copyright.htm', - 'docs/customizing.htm', - 'docs/debian.htm', - 'docs/devnotes.htm', - 'docs/hardware.htm', - 'docs/macos.htm', - 'docs/readme.htm', - 'docs/redhat.htm', - 'docs/setup.htm', - 'docs/suse.htm', - 'docs/upgrading.htm', - 'docs/usersguide.htm', - 'docs/utilities.htm']), - ('docs/css', - ['docs/css/tocbot-4.3.1.css', - 'docs/css/weewx_ui.css']), - ('docs/images', - ['docs/images/antialias.gif', - 'docs/images/day-gap-not-shown.png', - 'docs/images/day-gap-showing.png', - 'docs/images/daycompare.png', - 'docs/images/daytemp_with_avg.png', - 'docs/images/daywindvec.png', - 'docs/images/favicon.png', - 'docs/images/ferrites.jpg', - 'docs/images/funky_degree.png', - 'docs/images/image_parts.png', - 'docs/images/image_parts.xcf', - 'docs/images/logo-apple.png', - 'docs/images/logo-centos.png', - 'docs/images/logo-debian.png', - 'docs/images/logo-fedora.png', - 'docs/images/logo-linux.png', - 'docs/images/logo-mint.png', - 'docs/images/logo-opensuse.png', - 'docs/images/logo-pypi.svg', - 'docs/images/logo-redhat.png', - 'docs/images/logo-rpi.png', - 'docs/images/logo-suse.png', - 'docs/images/logo-ubuntu.png', - 'docs/images/logo-weewx.png', - 'docs/images/sample_monthrain.png', - 'docs/images/sample_monthtempdew.png', - 'docs/images/weekgustoverlay.png', - 'docs/images/weektempdew.png', - 'docs/images/yearhilow.png']), - ('docs/js', - ['docs/js/cash.js', - 'docs/js/tocbot-4.3.1.js', - 'docs/js/weewx.js']), - ('docs/examples', - ['docs/examples/tag.htm']), - ('examples', - ['examples/alarm.py', - 'examples/lowBattery.py', - 'examples/mem.py', - 'examples/stats.py', - 'examples/transfer_db.py']), - ('examples/basic', - ['examples/basic/changelog', - 'examples/basic/install.py', - 'examples/basic/readme.txt']), - ('examples/basic/skins/basic', - ['examples/basic/skins/basic/basic.css', - 'examples/basic/skins/basic/current.inc', - 'examples/basic/skins/basic/favicon.ico', - 'examples/basic/skins/basic/hilo.inc', - 'examples/basic/skins/basic/index.html.tmpl', - 'examples/basic/skins/basic/skin.conf']), - ('examples/fileparse', - ['examples/fileparse/changelog', - 'examples/fileparse/install.py', - 'examples/fileparse/readme.txt']), - ('examples/fileparse/bin/user', - ['examples/fileparse/bin/user/fileparse.py']), - ('examples/pmon', - ['examples/pmon/changelog', - 'examples/pmon/install.py', - 'examples/pmon/readme.txt']), - ('examples/pmon/bin/user', - ['examples/pmon/bin/user/pmon.py']), - ('examples/pmon/skins/pmon', - ['examples/pmon/skins/pmon/index.html.tmpl', - 'examples/pmon/skins/pmon/skin.conf']), - ('examples/xstats', - ['examples/xstats/changelog', - 'examples/xstats/install.py', - 'examples/xstats/readme.txt']), - ('examples/xstats/bin/user', - ['examples/xstats/bin/user/xstats.py']), - ('examples/xstats/skins/xstats', - ['examples/xstats/skins/xstats/index.html.tmpl', - 'examples/xstats/skins/xstats/skin.conf']), - ('skins/Ftp', - ['skins/Ftp/skin.conf']), - ('skins/Rsync', - ['skins/Rsync/skin.conf']), - ('skins/Smartphone', - ['skins/Smartphone/barometer.html.tmpl', - 'skins/Smartphone/custom.js', - 'skins/Smartphone/favicon.ico', - 'skins/Smartphone/humidity.html.tmpl', - 'skins/Smartphone/index.html.tmpl', - 'skins/Smartphone/rain.html.tmpl', - 'skins/Smartphone/skin.conf', - 'skins/Smartphone/temp.html.tmpl', - 'skins/Smartphone/wind.html.tmpl']), - ('skins/Smartphone/icons', - ['skins/Smartphone/icons/icon_ipad_x1.png', - 'skins/Smartphone/icons/icon_ipad_x2.png', - 'skins/Smartphone/icons/icon_iphone_x1.png', - 'skins/Smartphone/icons/icon_iphone_x2.png']), - ('skins/Seasons', - ['skins/Seasons/about.inc', - 'skins/Seasons/analytics.inc', - 'skins/Seasons/celestial.html.tmpl', - 'skins/Seasons/celestial.inc', - 'skins/Seasons/current.inc', - 'skins/Seasons/favicon.ico', - 'skins/Seasons/hilo.inc', - 'skins/Seasons/identifier.inc', - 'skins/Seasons/index.html.tmpl', - 'skins/Seasons/map.inc', - 'skins/Seasons/radar.inc', - 'skins/Seasons/rss.xml.tmpl', - 'skins/Seasons/satellite.inc', - 'skins/Seasons/sensors.inc', - 'skins/Seasons/skin.conf', - 'skins/Seasons/seasons.css', - 'skins/Seasons/seasons.js', - 'skins/Seasons/statistics.html.tmpl', - 'skins/Seasons/statistics.inc', - 'skins/Seasons/sunmoon.inc', - 'skins/Seasons/tabular.html.tmpl', - 'skins/Seasons/telemetry.html.tmpl', - 'skins/Seasons/titlebar.inc']), - ('skins/Seasons/NOAA', - ['skins/Seasons/NOAA/NOAA-%Y-%m.txt.tmpl', - 'skins/Seasons/NOAA/NOAA-%Y.txt.tmpl']), - ('skins/Seasons/font', - ['skins/Seasons/font/OpenSans-Bold.ttf', - 'skins/Seasons/font/OpenSans-Regular.ttf', - 'skins/Seasons/font/OpenSans.woff', - 'skins/Seasons/font/OpenSans.woff2']), - ('skins/Mobile', - ['skins/Mobile/favicon.ico', - 'skins/Mobile/index.html.tmpl', - 'skins/Mobile/skin.conf', - 'skins/Mobile/mobile.css']), - ('skins/Standard', - ['skins/Standard/favicon.ico', - 'skins/Standard/index.html.tmpl', - 'skins/Standard/mobile.css', - 'skins/Standard/mobile.html.tmpl', - 'skins/Standard/month.html.tmpl', - 'skins/Standard/skin.conf', - 'skins/Standard/week.html.tmpl', - 'skins/Standard/weewx.css', - 'skins/Standard/year.html.tmpl']), - ('skins/Standard/NOAA', - ['skins/Standard/NOAA/NOAA-%Y-%m.txt.tmpl', - 'skins/Standard/NOAA/NOAA-%Y.txt.tmpl']), - ('skins/Standard/RSS', - ['skins/Standard/RSS/weewx_rss.xml.tmpl']), - ('skins/Standard/backgrounds', - ['skins/Standard/backgrounds/band.gif', - 'skins/Standard/backgrounds/butterfly.jpg', - 'skins/Standard/backgrounds/drops.gif', - 'skins/Standard/backgrounds/flower.jpg', - 'skins/Standard/backgrounds/leaf.jpg', - 'skins/Standard/backgrounds/night.gif']), - ('skins/Standard/smartphone', - ['skins/Standard/smartphone/barometer.html.tmpl', - 'skins/Standard/smartphone/custom.js', - 'skins/Standard/smartphone/humidity.html.tmpl', - 'skins/Standard/smartphone/index.html.tmpl', - 'skins/Standard/smartphone/radar.html.tmpl', - 'skins/Standard/smartphone/rain.html.tmpl', - 'skins/Standard/smartphone/temp_outside.html.tmpl', - 'skins/Standard/smartphone/wind.html.tmpl']), - ('skins/Standard/smartphone/icons', - ['skins/Standard/smartphone/icons/icon_ipad_x1.png', - 'skins/Standard/smartphone/icons/icon_ipad_x2.png', - 'skins/Standard/smartphone/icons/icon_iphone_x1.png', - 'skins/Standard/smartphone/icons/icon_iphone_x2.png']), - ('util/apache/conf.d', - ['util/apache/conf.d/weewx.conf']), - ('util/import', - ['util/import/csv-example.conf', - 'util/import/cumulus-example.conf', - 'util/import/wd-example.conf', - 'util/import/wu-example.conf']), - ('util/init.d', - ['util/init.d/weewx.bsd', - 'util/init.d/weewx.debian', - 'util/init.d/weewx.lsb', - 'util/init.d/weewx.redhat', - 'util/init.d/weewx.suse']), - ('util/launchd', - ['util/launchd/com.weewx.weewxd.plist']), - ('util/logrotate.d', - ['util/logrotate.d/weewx']), - ('util/logwatch/conf/logfiles', - ['util/logwatch/conf/logfiles/weewx.conf']), - ('util/logwatch/conf/services', - ['util/logwatch/conf/services/weewx.conf']), - ('util/logwatch/scripts/services', - ['util/logwatch/scripts/services/weewx']), - ('util/newsyslog.d', - ['util/newsyslog.d/weewx.conf']), - ('util/rsyslog.d', - ['util/rsyslog.d/weewx.conf']), - ('util/solaris', - ['util/solaris/weewx-smf.xml']), - ('util/systemd', - ['util/systemd/weewx.service']), - ('util/udev/rules.d', - ['util/udev/rules.d/acurite.rules', - 'util/udev/rules.d/cc3000.rules', - 'util/udev/rules.d/fousb.rules', - 'util/udev/rules.d/te923.rules', - 'util/udev/rules.d/vantage.rules', - 'util/udev/rules.d/wmr100.rules', - 'util/udev/rules.d/wmr200.rules', - 'util/udev/rules.d/wmr300.rules', - 'util/udev/rules.d/ws28xx.rules']) - ] + data_files=[('', ['LICENSE.txt', 'README.md', 'weewx.conf']), ] + + find_files('docs') + + find_files('examples') + + find_files('skins') + + find_files('util'), + cmdclass={ + "install": weewx_install, + "install_data": weewx_install_data, + "install_lib": weewx_install_lib, + }, )