From 012005222540d95203d8abcc3380101bc2f0890e Mon Sep 17 00:00:00 2001 From: Tom Keffer Date: Sat, 30 Dec 2023 17:22:59 -0800 Subject: [PATCH 1/3] Refactor. Put station actions in weectllib alongside the other actions --- .../station_actions.py} | 0 src/weectllib/station_cmd.py | 88 ++++++------- .../tests/test_station_actions.py} | 118 +++++++++--------- 3 files changed, 104 insertions(+), 102 deletions(-) rename src/{weecfg/station_config.py => weectllib/station_actions.py} (100%) rename src/{weecfg/tests/test_station_config.py => weectllib/tests/test_station_actions.py} (73%) diff --git a/src/weecfg/station_config.py b/src/weectllib/station_actions.py similarity index 100% rename from src/weecfg/station_config.py rename to src/weectllib/station_actions.py diff --git a/src/weectllib/station_cmd.py b/src/weectllib/station_cmd.py index 8641d635..4ced9523 100644 --- a/src/weectllib/station_cmd.py +++ b/src/weectllib/station_cmd.py @@ -7,8 +7,9 @@ import os.path import sys -import weecfg.station_config +import weecfg import weectllib +import weectllib.station_actions import weewx from weeutil.weeutil import bcolors @@ -194,23 +195,24 @@ def add_subparser(subparsers): def create_station(namespace): """Map 'namespace' to a call to station_create()""" try: - config_dict = weecfg.station_config.station_create(config_path=namespace.config, - dist_config_path=namespace.dist_config, - driver=namespace.driver, - location=namespace.location, - altitude=namespace.altitude, - latitude=namespace.latitude, - longitude=namespace.longitude, - register=namespace.register, - station_url=namespace.station_url, - unit_system=namespace.unit_system, - weewx_root=namespace.weewx_root, - skin_root=namespace.skin_root, - sqlite_root=namespace.sqlite_root, - html_root=namespace.html_root, - examples_root=namespace.examples_root, - no_prompt=namespace.no_prompt, - dry_run=namespace.dry_run) + config_dict \ + = weectllib.station_actions.station_create(config_path=namespace.config, + dist_config_path=namespace.dist_config, + driver=namespace.driver, + location=namespace.location, + altitude=namespace.altitude, + latitude=namespace.latitude, + longitude=namespace.longitude, + register=namespace.register, + station_url=namespace.station_url, + unit_system=namespace.unit_system, + weewx_root=namespace.weewx_root, + skin_root=namespace.skin_root, + sqlite_root=namespace.sqlite_root, + html_root=namespace.html_root, + examples_root=namespace.examples_root, + no_prompt=namespace.no_prompt, + dry_run=namespace.dry_run) except weewx.ViolatedPrecondition as e: sys.exit(e) script_dir = os.path.join(config_dict["WEEWX_ROOT"], "scripts") @@ -232,35 +234,35 @@ def create_station(namespace): def reconfigure_station(config_dict, namespace): """Map namespace to a call to station_reconfigure()""" try: - weecfg.station_config.station_reconfigure(config_dict=config_dict, - driver=namespace.driver, - location=namespace.location, - altitude=namespace.altitude, - latitude=namespace.latitude, - longitude=namespace.longitude, - register=namespace.register, - station_url=namespace.station_url, - unit_system=namespace.unit_system, - weewx_root=namespace.weewx_root, - skin_root=namespace.skin_root, - sqlite_root=namespace.sqlite_root, - html_root=namespace.html_root, - no_prompt=namespace.no_prompt, - no_backup=namespace.no_backup, - dry_run=namespace.dry_run) + weectllib.station_actions.station_reconfigure(config_dict=config_dict, + driver=namespace.driver, + location=namespace.location, + altitude=namespace.altitude, + latitude=namespace.latitude, + longitude=namespace.longitude, + register=namespace.register, + station_url=namespace.station_url, + unit_system=namespace.unit_system, + weewx_root=namespace.weewx_root, + skin_root=namespace.skin_root, + sqlite_root=namespace.sqlite_root, + html_root=namespace.html_root, + no_prompt=namespace.no_prompt, + no_backup=namespace.no_backup, + dry_run=namespace.dry_run) except weewx.ViolatedPrecondition as e: - sys.exit(e) + sys.exit(str(e)) def upgrade_station(config_dict, namespace): - weecfg.station_config.station_upgrade(config_dict=config_dict, - dist_config_path=namespace.dist_config, - examples_root=namespace.examples_root, - skin_root=namespace.skin_root, - what=namespace.what, - no_prompt=namespace.no_prompt, - no_backup=namespace.no_backup, - dry_run=namespace.dry_run) + weectllib.station_actions.station_upgrade(config_dict=config_dict, + dist_config_path=namespace.dist_config, + examples_root=namespace.examples_root, + skin_root=namespace.skin_root, + what=namespace.what, + no_prompt=namespace.no_prompt, + no_backup=namespace.no_backup, + dry_run=namespace.dry_run) # ============================================================================== diff --git a/src/weecfg/tests/test_station_config.py b/src/weectllib/tests/test_station_actions.py similarity index 73% rename from src/weecfg/tests/test_station_config.py rename to src/weectllib/tests/test_station_actions.py index dcf1a31f..0d1ecb1a 100644 --- a/src/weecfg/tests/test_station_config.py +++ b/src/weectllib/tests/test_station_actions.py @@ -13,9 +13,7 @@ from unittest.mock import patch import configobj -import weecfg.extension -import weecfg.station_config -import weecfg.update_config +import weectllib.station_actions import weeutil.config import weeutil.weeutil import weewx @@ -48,75 +46,75 @@ class CommonConfigTest(unittest.TestCase): class LocationConfigTest(CommonConfigTest): def test_default_config_location(self): - weecfg.station_config.config_location(self.config_dict, no_prompt=True) + weectllib.station_actions.config_location(self.config_dict, no_prompt=True) self.assertEqual(self.config_dict['Station']['location'], "WeeWX station") del self.config_dict['Station']['location'] - weecfg.station_config.config_location(self.config_dict, no_prompt=True) + weectllib.station_actions.config_location(self.config_dict, no_prompt=True) self.assertEqual(self.config_dict['Station']['location'], "WeeWX station") def test_arg_config_location(self): - weecfg.station_config.config_location(self.config_dict, location='foo', no_prompt=True) + weectllib.station_actions.config_location(self.config_dict, location='foo', no_prompt=True) self.assertEqual(self.config_dict['Station']['location'], "foo") @suppress_stdout def test_prompt_config_location(self): - with patch('weecfg.station_config.input', side_effect=['']): - weecfg.station_config.config_location(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['']): + weectllib.station_actions.config_location(self.config_dict) self.assertEqual(self.config_dict['Station']['location'], "WeeWX station") - with patch('weecfg.station_config.input', side_effect=['bar']): - weecfg.station_config.config_location(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['bar']): + weectllib.station_actions.config_location(self.config_dict) self.assertEqual(self.config_dict['Station']['location'], "bar") class AltitudeConfigTest(CommonConfigTest): def test_default_config_altitude(self): - weecfg.station_config.config_altitude(self.config_dict, no_prompt=True) + weectllib.station_actions.config_altitude(self.config_dict, no_prompt=True) self.assertEqual(self.config_dict['Station']['altitude'], ["0", "foot"]) # Delete the value in the configuration dictionary del self.config_dict['Station']['altitude'] # Now we should get the hardwired default - weecfg.station_config.config_altitude(self.config_dict, no_prompt=True) + weectllib.station_actions.config_altitude(self.config_dict, no_prompt=True) self.assertEqual(self.config_dict['Station']['altitude'], ["0", "foot"]) def test_arg_config_altitude(self): - weecfg.station_config.config_altitude(self.config_dict, altitude="500, meter") + weectllib.station_actions.config_altitude(self.config_dict, altitude="500, meter") self.assertEqual(self.config_dict['Station']['altitude'], ["500", "meter"]) def test_badarg_config_altitude(self): with self.assertRaises(ValueError): # Bad unit - weecfg.station_config.config_altitude(self.config_dict, altitude="500, foo") + weectllib.station_actions.config_altitude(self.config_dict, altitude="500, foo") with self.assertRaises(ValueError): # Bad value - weecfg.station_config.config_altitude(self.config_dict, altitude="500f, foot") + weectllib.station_actions.config_altitude(self.config_dict, altitude="500f, foot") @suppress_stdout def test_prompt_config_altitude(self): - with patch('weecfg.station_config.input', side_effect=['']): - weecfg.station_config.config_altitude(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['']): + weectllib.station_actions.config_altitude(self.config_dict) self.assertEqual(self.config_dict['Station']['altitude'], ["0", "foot"]) - with patch('weecfg.station_config.input', side_effect=['110, meter']): - weecfg.station_config.config_altitude(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['110, meter']): + weectllib.station_actions.config_altitude(self.config_dict) self.assertEqual(self.config_dict['Station']['altitude'], ["110", "meter"]) # Try 'feet' instead of 'foot' - with patch('weecfg.station_config.input', side_effect=['700, feet']): - weecfg.station_config.config_altitude(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['700, feet']): + weectllib.station_actions.config_altitude(self.config_dict) self.assertEqual(self.config_dict['Station']['altitude'], ["700", "foot"]) # Try 'meters' instead of 'meter': - with patch('weecfg.station_config.input', side_effect=['110, meters']): - weecfg.station_config.config_altitude(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['110, meters']): + weectllib.station_actions.config_altitude(self.config_dict) self.assertEqual(self.config_dict['Station']['altitude'], ["110", "meter"]) @suppress_stdout def test_badprompt_config_altitude(self): # Include a bad unit. It should prompt again - with patch('weecfg.station_config.input', side_effect=['100, foo', '110, meter']): - weecfg.station_config.config_altitude(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['100, foo', '110, meter']): + weectllib.station_actions.config_altitude(self.config_dict) self.assertEqual(self.config_dict['Station']['altitude'], ["110", "meter"]) # Include a bad value. It should prompt again - with patch('weecfg.station_config.input', side_effect=['100f, foot', '110, meter']): - weecfg.station_config.config_altitude(self.config_dict) + with patch('weectllib.station_actions.input', side_effect=['100f, foot', '110, meter']): + weectllib.station_actions.config_altitude(self.config_dict) self.assertEqual(self.config_dict['Station']['altitude'], ["110", "meter"]) @@ -124,30 +122,31 @@ class LatLonConfigTest(CommonConfigTest): def test_default_config_latlon(self): # Use the default as supplied by CONFIG_DICT - weecfg.station_config.config_latlon(self.config_dict, no_prompt=True) + weectllib.station_actions.config_latlon(self.config_dict, no_prompt=True) self.assertEqual(float(self.config_dict['Station']['latitude']), 0.0) self.assertEqual(float(self.config_dict['Station']['longitude']), 0.0) # Delete the values in the configuration dictionary del self.config_dict['Station']['latitude'] del self.config_dict['Station']['longitude'] # Now the defaults should be the hardwired defaults - weecfg.station_config.config_latlon(self.config_dict, no_prompt=True) + weectllib.station_actions.config_latlon(self.config_dict, no_prompt=True) self.assertEqual(float(self.config_dict['Station']['latitude']), 0.0) self.assertEqual(float(self.config_dict['Station']['longitude']), 0.0) def test_arg_config_latlon(self): - weecfg.station_config.config_latlon(self.config_dict, latitude='-20', longitude='-40') + weectllib.station_actions.config_latlon(self.config_dict, latitude='-20', longitude='-40') self.assertEqual(float(self.config_dict['Station']['latitude']), -20.0) self.assertEqual(float(self.config_dict['Station']['longitude']), -40.0) def test_badarg_config_latlon(self): with self.assertRaises(ValueError): - weecfg.station_config.config_latlon(self.config_dict, latitude="-20f", longitude='-40') + weectllib.station_actions.config_latlon(self.config_dict, latitude="-20f", + longitude='-40') @suppress_stdout def test_prompt_config_latlong(self): with patch('weecfg.input', side_effect=['-21', '-41']): - weecfg.station_config.config_latlon(self.config_dict) + weectllib.station_actions.config_latlon(self.config_dict) self.assertEqual(float(self.config_dict['Station']['latitude']), -21.0) self.assertEqual(float(self.config_dict['Station']['longitude']), -41.0) @@ -155,23 +154,24 @@ class LatLonConfigTest(CommonConfigTest): class RegistryConfigTest(CommonConfigTest): def test_default_register(self): - weecfg.station_config.config_registry(self.config_dict, no_prompt=True) + weectllib.station_actions.config_registry(self.config_dict, no_prompt=True) self.assertFalse( self.config_dict['StdRESTful']['StationRegistry']['register_this_station']) def test_args_register(self): # Missing station_url: with self.assertRaises(weewx.ViolatedPrecondition): - weecfg.station_config.config_registry(self.config_dict, register='True', - no_prompt=True) + weectllib.station_actions.config_registry(self.config_dict, register='True', + no_prompt=True) # This time we supply a station_url. Should be OK. - weecfg.station_config.config_registry(self.config_dict, register='True', - station_url=STATION_URL, no_prompt=True) + weectllib.station_actions.config_registry(self.config_dict, register='True', + station_url=STATION_URL, no_prompt=True) self.assertTrue(self.config_dict['StdRESTful']['StationRegistry']['register_this_station']) self.assertEqual(self.config_dict['Station']['station_url'], STATION_URL) # Alternatively, the config file already had a station_url: self.config_dict['Station']['station_url'] = STATION_URL - weecfg.station_config.config_registry(self.config_dict, register='True', no_prompt=True) + weectllib.station_actions.config_registry(self.config_dict, register='True', + no_prompt=True) self.assertTrue(self.config_dict['StdRESTful']['StationRegistry']['register_this_station']) self.assertEqual(self.config_dict['Station']['station_url'], STATION_URL) @@ -179,21 +179,21 @@ class RegistryConfigTest(CommonConfigTest): def test_prompt_register(self): with patch('weeutil.weeutil.input', side_effect=['y']): with patch('weecfg.input', side_effect=[STATION_URL]): - weecfg.station_config.config_registry(self.config_dict) + weectllib.station_actions.config_registry(self.config_dict) self.assertTrue(self.config_dict['StdRESTful']['StationRegistry']['register_this_station']) self.assertEqual(self.config_dict['Station']['station_url'], STATION_URL) # Try again, but without specifying an URL. Should ask twice. with patch('weeutil.weeutil.input', side_effect=['y']): with patch('weecfg.input', side_effect=["", STATION_URL]): - weecfg.station_config.config_registry(self.config_dict) + weectllib.station_actions.config_registry(self.config_dict) self.assertTrue(self.config_dict['StdRESTful']['StationRegistry']['register_this_station']) self.assertEqual(self.config_dict['Station']['station_url'], STATION_URL) # Now with a bogus URL with patch('weeutil.weeutil.input', side_effect=['y']): with patch('weecfg.input', side_effect=['https://www.example.com', STATION_URL]): - weecfg.station_config.config_registry(self.config_dict) + weectllib.station_actions.config_registry(self.config_dict) self.assertTrue(self.config_dict['StdRESTful']['StationRegistry']['register_this_station']) self.assertEqual(self.config_dict['Station']['station_url'], STATION_URL) @@ -201,40 +201,40 @@ class RegistryConfigTest(CommonConfigTest): class UnitsConfigTest(CommonConfigTest): def test_default_units(self): - weecfg.station_config.config_units(self.config_dict, no_prompt=True) + weectllib.station_actions.config_units(self.config_dict, no_prompt=True) self.assertEqual(self.config_dict['StdReport']['Defaults']['unit_system'], 'us') def test_custom_units(self): del self.config_dict['StdReport']['Defaults']['unit_system'] - weecfg.station_config.config_units(self.config_dict, no_prompt=True) + weectllib.station_actions.config_units(self.config_dict, no_prompt=True) self.assertNotIn('unit_system', self.config_dict['StdReport']['Defaults']) def test_args_units(self): - weecfg.station_config.config_units(self.config_dict, unit_system='metricwx', - no_prompt=True) + weectllib.station_actions.config_units(self.config_dict, unit_system='metricwx', + no_prompt=True) self.assertEqual(self.config_dict['StdReport']['Defaults']['unit_system'], 'metricwx') @suppress_stdout def test_prompt_units(self): with patch('weecfg.input', side_effect=['metricwx']): - weecfg.station_config.config_units(self.config_dict) + weectllib.station_actions.config_units(self.config_dict) self.assertEqual(self.config_dict['StdReport']['Defaults']['unit_system'], 'metricwx') # Do it again, but with a wrong unit system name. It should ask again. with patch('weecfg.input', side_effect=['metricwz', 'metricwx']): - weecfg.station_config.config_units(self.config_dict) + weectllib.station_actions.config_units(self.config_dict) self.assertEqual(self.config_dict['StdReport']['Defaults']['unit_system'], 'metricwx') class DriverConfigTest(CommonConfigTest): def test_default_config_driver(self): - weecfg.station_config.config_driver(self.config_dict, no_prompt=True) + weectllib.station_actions.config_driver(self.config_dict, no_prompt=True) self.assertEqual(self.config_dict['Station']['station_type'], 'Simulator') self.assertEqual(self.config_dict['Simulator']['driver'], 'weewx.drivers.simulator') def test_arg_config_driver(self): - weecfg.station_config.config_driver(self.config_dict, driver='weewx.drivers.vantage', - no_prompt=True) + weectllib.station_actions.config_driver(self.config_dict, driver='weewx.drivers.vantage', + no_prompt=True) self.assertEqual(self.config_dict['Station']['station_type'], 'Vantage') self.assertEqual(self.config_dict['Vantage']['driver'], 'weewx.drivers.vantage') @@ -246,9 +246,9 @@ class DriverConfigTest(CommonConfigTest): del weewx.drivers.vantage.confeditor_loader # At this point, there is no configuration loader, so a minimal version of [Vantage] # should be supplied. - weecfg.station_config.config_driver(self.config_dict, - driver='weewx.drivers.vantage', - no_prompt=True) + weectllib.station_actions.config_driver(self.config_dict, + driver='weewx.drivers.vantage', + no_prompt=True) self.assertEqual(self.config_dict['Station']['station_type'], 'Vantage') self.assertEqual(self.config_dict['Vantage']['driver'], 'weewx.drivers.vantage') # The rest of the [Vantage] stanza should be missing. Try a key. @@ -259,13 +259,13 @@ class DriverConfigTest(CommonConfigTest): @suppress_stdout def test_prompt_config_driver(self): with patch('weecfg.input', side_effect=['6', '', '/dev/ttyS0']): - weecfg.station_config.config_driver(self.config_dict) + weectllib.station_actions.config_driver(self.config_dict) self.assertEqual(self.config_dict['Station']['station_type'], 'Vantage') self.assertEqual(self.config_dict['Vantage']['port'], '/dev/ttyS0') # Do it again. This time, the stanza ['Vantage'] will exist, and we'll just modify it with patch('weecfg.input', side_effect=['', '', '/dev/ttyS1']): - weecfg.station_config.config_driver(self.config_dict) + weectllib.station_actions.config_driver(self.config_dict) self.assertEqual(self.config_dict['Station']['station_type'], 'Vantage') self.assertEqual(self.config_dict['Vantage']['port'], '/dev/ttyS1') @@ -273,8 +273,8 @@ class DriverConfigTest(CommonConfigTest): class TestConfigRoots(CommonConfigTest): def test_args_config_roots(self): - weecfg.station_config.config_roots(self.config_dict, skin_root='foo', - html_root='bar', sqlite_root='baz') + weectllib.station_actions.config_roots(self.config_dict, skin_root='foo', + html_root='bar', sqlite_root='baz') self.assertEqual(self.config_dict['StdReport']['SKIN_ROOT'], 'foo') self.assertEqual(self.config_dict['StdReport']['HTML_ROOT'], 'bar') self.assertEqual(self.config_dict['DatabaseTypes']['SQLite']['SQLITE_ROOT'], 'baz') @@ -283,7 +283,7 @@ class TestConfigRoots(CommonConfigTest): del self.config_dict['StdReport']['SKIN_ROOT'] del self.config_dict['StdReport']['HTML_ROOT'] del self.config_dict['DatabaseTypes']['SQLite']['SQLITE_ROOT'] - weecfg.station_config.config_roots(self.config_dict) + weectllib.station_actions.config_roots(self.config_dict) self.assertEqual(self.config_dict['StdReport']['SKIN_ROOT'], 'skins') self.assertEqual(self.config_dict['StdReport']['HTML_ROOT'], 'public_html') self.assertEqual(self.config_dict['DatabaseTypes']['SQLite']['SQLITE_ROOT'], @@ -299,7 +299,7 @@ class TestCreateStation(unittest.TestCase): config_path = os.path.join(dirname, 'weewx.conf') # We have not run 'pip', so the only copy of weewxd.py is the one in the repository. # Create a station using the defaults - weecfg.station_config.station_create(config_path, no_prompt=True) + weectllib.station_actions.station_create(config_path, no_prompt=True) # Retrieve the config file that was created and check it: config_dict = configobj.ConfigObj(config_path, encoding='utf-8') From 443f8e417f726464cf57242db38f6c7e02df42d4 Mon Sep 17 00:00:00 2001 From: Tom Keffer Date: Sat, 30 Dec 2023 17:23:40 -0800 Subject: [PATCH 2/3] Rename. It was getting missed by "make test"! --- src/weectllib/tests/{weectllib_tests.py => test_weectllib.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/weectllib/tests/{weectllib_tests.py => test_weectllib.py} (100%) diff --git a/src/weectllib/tests/weectllib_tests.py b/src/weectllib/tests/test_weectllib.py similarity index 100% rename from src/weectllib/tests/weectllib_tests.py rename to src/weectllib/tests/test_weectllib.py index f68ad1d9..5e3951d7 100644 --- a/src/weectllib/tests/weectllib_tests.py +++ b/src/weectllib/tests/test_weectllib.py @@ -4,8 +4,8 @@ # See the file LICENSE.txt for your full rights. # -import unittest import datetime +import unittest from weectllib import parse_dates From fbe01fabbad9427fefa3682dee61d1218333b1f6 Mon Sep 17 00:00:00 2001 From: Tom Keffer Date: Tue, 2 Jan 2024 05:54:59 -0800 Subject: [PATCH 3/3] Make WEEWX-ROOT relative to location of the config file Stn now created using "weectl station create WEEWX-ROOT" --- TODO.md | 11 +- src/weectllib/station_actions.py | 176 ++++++++---- src/weectllib/station_cmd.py | 291 +++++++++++--------- src/weectllib/tests/test_station_actions.py | 19 +- src/weewx_data/weewx.conf | 2 +- 5 files changed, 304 insertions(+), 195 deletions(-) diff --git a/TODO.md b/TODO.md index d30a4761..3cfdb0de 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,14 @@ # V5.0 "To Do" -## startup -- tk before logger is initialized, output to stdout/stderr as appropriate -- tk Rationalize startup procedure, making it consistent. +## Docs + +- tk WEEWX-ROOT is now relative to the location of the config file. Reflect + this in docs + +## weectl + +- mw Resolve "FIXME" in `station_actions.copy_utils()` ## deb/rpm installs diff --git a/src/weectllib/station_actions.py b/src/weectllib/station_actions.py index 1b1951d9..8d5c9e25 100644 --- a/src/weectllib/station_actions.py +++ b/src/weectllib/station_actions.py @@ -7,13 +7,12 @@ import getpass import grp -import importlib import logging import os -import stat import os.path import re import shutil +import stat import sys import urllib.parse @@ -34,44 +33,31 @@ from weeutil.weeutil import to_float, to_bool, bcolors log = logging.getLogger('weectl-station') -def station_create(config_path, *args, +def station_create(weewx_root=weecfg.default_weewx_root, + rel_config_path='./weewx.conf', + driver='weewx.drivers.simulator', + location='WeeWX station', + altitude='0, foot', + latitude=0, longitude=0, + register=False, station_url='https://example.com', + unit_system='us', + skin_root='./skins', + sqlite_root='./archive', + html_root='./public_html', + examples_root='./examples', + user_root='./bin/user', dist_config_path=None, - weewx_root=None, - examples_root=None, - user_root=None, - dry_run=False, - **kwargs): - """Create a brand-new station by creating a new configuration file. - - If a value of weewx_root is not given, then it will be chosen as the - directory the resultant configuration file is in. - - This function first checks whether the configuration file already exists. - If it does, then an exception is raised. - - It then: - 1. If no_prompt is false, it creates the configuration file by prompting - the user. If true, it uses defaults. - 2. Copies the examples and utility files out of package resources and - into WEEWX_ROOT. - """ + no_prompt=False, + dry_run=False): + """Create a brand-new station data area at weewx_root, then equip it with a + configuration file.""" if dry_run: print("This is a dry run. Nothing will actually be done.") - # If no configuration file was specified, use the default (which is only - # 'correct' for pip/git installs). - if not config_path: - config_path = weecfg.default_config_path - - # If a value of WEEWX_ROOT was specified, use that, overriding whatever - # might have been specified for the configuration file. Otherwise, use the - # directory in which the configuration file resides. - if weewx_root: - _, filename = os.path.split(config_path) - config_path = os.path.join(weewx_root, filename) - else: - weewx_root = os.path.abspath(os.path.dirname(config_path)) + # Invert the relationship between weewx_root and the relative path to the config file. + # When done, weewx_root will be relative to the directory the config file will be in. + config_path, rel_weewx_root = _calc_paths(weewx_root, rel_config_path) # Make sure there is not a configuration file at the designated location. if os.path.exists(config_path): @@ -81,7 +67,7 @@ def station_create(config_path, *args, # If a distribution configuration was specified, use the contents from that # for the new configuration. Otherwise, extract the contents from the - # config in the python package resources. + # configuration file in the python package resources. if dist_config_path: dist_config_dict = configobj.ConfigObj(dist_config_path, encoding='utf-8', file_error=True) else: @@ -89,8 +75,24 @@ def station_create(config_path, *args, with weeutil.weeutil.get_resource_fd('weewx_data', 'weewx.conf') as fd: dist_config_dict = configobj.ConfigObj(fd, encoding='utf-8', file_error=True) - config_config(config_path, dist_config_dict, weewx_root=weewx_root, - dry_run=dry_run, *args, **kwargs) + dist_config_dict['WEEWX_ROOT_ORIG'] = rel_weewx_root + config_dir = os.path.dirname(config_path) + dist_config_dict['WEEWX_ROOT'] = os.path.abspath(os.path.join(config_dir, rel_weewx_root)) + + # Modify the configuration dictionary config_dict, prompting if necessary. + config_config(config_dict=dist_config_dict, + config_path=config_path, + driver=driver, + location=location, + altitude=altitude, + latitude=latitude, longitude=longitude, + register=register, station_url=station_url, + unit_system=unit_system, + skin_root=skin_root, + sqlite_root=sqlite_root, + html_root=html_root, + user_root=user_root, + no_prompt=no_prompt) copy_skins(dist_config_dict, dry_run=dry_run) copy_examples(dist_config_dict, examples_root=examples_root, dry_run=dry_run) copy_user(dist_config_dict, user_root=user_root, dry_run=dry_run) @@ -105,10 +107,73 @@ def station_create(config_path, *args, return dist_config_dict -def station_reconfigure(config_dict, no_backup=False, dry_run=False, *args, **kwargs): +def _calc_paths(weewx_root=None, rel_config_path=None): + """When creating a station, the config path is specified relative to WEEWX_ROOT. + However, within the configuration file, WEEWX_ROOT is relative to the location + of the config file. So, we need to invert their relationships. + + Args: + weewx_root (str): A path to the station data area. + rel_config_path (str): A path relative to weewx_root where the configuration + file will be located. + + Returns: + tuple[str, str]: A 2-way tuple containing the absolute path to the config file, and + the path to the station data area, relative to the config file + """ + # Apply defaults if necessary: + if not weewx_root: + weewx_root = weecfg.default_weewx_root + if not rel_config_path: + rel_config_path = './weewx.conf' + + # Convert to absolute paths + abs_weewx_root = os.path.abspath(weewx_root) + abs_config_path = os.path.abspath(os.path.join(abs_weewx_root, rel_config_path)) + + # Get WEEWX_ROOT relative to the directory the configuration file is sitting in: + final_weewx_root = os.path.relpath(weewx_root, os.path.dirname(abs_config_path)) + # Don't let the relative path get out of hand. If we have to ascend more than two levels, + # use the absolute path: + if '../..' in final_weewx_root: + final_weewx_root = abs_weewx_root + if final_weewx_root == '.': + final_weewx_root = './' + return abs_config_path, final_weewx_root + + +def station_reconfigure(config_dict, + driver, + location, + altitude, + latitude, longitude, + register, station_url, + unit_system, + weewx_root, + skin_root, + sqlite_root, + html_root, + user_root, + no_prompt=False, + no_backup=False, + dry_run=False): """Reconfigure an existing station""" - config_config(config_dict['config_path'], config_dict, dry_run=dry_run, *args, **kwargs) + if weewx_root: + config_dict['WEEWX_ROOT'] = config_dict['WEEWX_ROOT_ORIG'] = weewx_root + config_config(config_dict, + config_path=config_dict['config_path'], + driver=driver, + location=location, + altitude=altitude, + latitude=latitude, longitude=longitude, + register=register, station_url=station_url, + unit_system=unit_system, + skin_root=skin_root, + sqlite_root=sqlite_root, + html_root=html_root, + user_root=user_root, + no_prompt=no_prompt) print(f"Saving configuration file {config_dict['config_path']}") if dry_run: @@ -120,17 +185,20 @@ def station_reconfigure(config_dict, no_backup=False, dry_run=False, *args, **kw print(f"Saved old configuration file as {backup_path}") -def config_config(config_path, config_dict, - driver=None, location=None, - altitude=None, latitude=None, longitude=None, +def config_config(config_dict, + config_path, + driver=None, + location=None, + altitude=None, + latitude=None, longitude=None, register=None, station_url=None, unit_system=None, - weewx_root=None, skin_root=None, - html_root=None, sqlite_root=None, + skin_root=None, + sqlite_root=None, + html_root=None, user_root=None, - no_prompt=False, - dry_run=False): - """Modify a configuration file.""" + no_prompt=False): + """Set the various options in a configuration file""" print(f"Processing configuration file {config_path}") config_location(config_dict, location=location, no_prompt=no_prompt) config_altitude(config_dict, altitude=altitude, no_prompt=no_prompt) @@ -138,7 +206,7 @@ def config_config(config_path, config_dict, config_units(config_dict, unit_system=unit_system, no_prompt=no_prompt) config_driver(config_dict, driver=driver, no_prompt=no_prompt) config_registry(config_dict, register=register, station_url=station_url, no_prompt=no_prompt) - config_roots(config_dict, weewx_root, skin_root, html_root, sqlite_root, user_root) + config_roots(config_dict, skin_root, html_root, sqlite_root, user_root) def config_location(config_dict, location=None, no_prompt=False): @@ -456,15 +524,9 @@ def config_registry(config_dict, register=None, station_url=None, no_prompt=Fals weecfg.inject_station_url(config_dict, final_station_url) -def config_roots(config_dict, - weewx_root=None, - skin_root=None, - html_root=None, - sqlite_root=None, - user_root=None): +def config_roots(config_dict, skin_root=None, html_root=None, sqlite_root=None, user_root=None): """Set the location of various root directories in the configuration dictionary.""" - if weewx_root: - config_dict['WEEWX_ROOT'] = weewx_root + if user_root: config_dict['USER_ROOT'] = user_root diff --git a/src/weectllib/station_cmd.py b/src/weectllib/station_cmd.py index fc5b3e09..5b58a039 100644 --- a/src/weectllib/station_cmd.py +++ b/src/weectllib/station_cmd.py @@ -13,14 +13,13 @@ import weectllib.station_actions import weewx from weeutil.weeutil import bcolors -station_create_usage = f"""{bcolors.BOLD}weectl station create +station_create_usage = f"""{bcolors.BOLD}weectl station create [WEEWX-ROOT] [--driver=DRIVER] [--location=LOCATION] [--altitude=ALTITUDE,(foot|meter)] [--latitude=LATITUDE] [--longitude=LONGITUDE] [--register=(y,n) [--station-url=URL]] [--units=(us|metricwx|metric)] - [--weewx-root=DIRECTORY] [--skin-root=DIRECTORY] [--sqlite-root=DIRECTORY] [--html-root=DIRECTORY] @@ -38,10 +37,11 @@ station_reconfigure_usage = f"""{bcolors.BOLD}weectl station reconfigure [--latitude=LATITUDE] [--longitude=LONGITUDE] [--register=(y,n) [--station-url=URL]] [--units=(us|metricwx|metric)] - [--weewx-root=DIRECTORY] [--skin-root=DIRECTORY] [--sqlite-root=DIRECTORY] [--html-root=DIRECTORY] + [--user-root=DIRECTORY] + [--weewx-root=DIRECTORY] [--no-backup] [--no-prompt] [--config=FILENAME] @@ -61,15 +61,18 @@ station_upgrade_usage = f"""{bcolors.BOLD}weectl station upgrade station_usage = '\n '.join((station_create_usage, station_reconfigure_usage, station_upgrade_usage)) -WEEWX_ROOT_DESCRIPTION = f"""In what follows, {bcolors.BOLD}WEEWX_ROOT{bcolors.ENDC} is the -directory that contains the configuration file. For example, if -"--config={weecfg.default_config_path}", then WEEWX_ROOT will be "{weecfg.default_weewx_root}".""" +CREATE_DESCRIPTION = f"""Create a new station data area at the location WEEWX-ROOT. If WEEWX-ROOT +is not provided, the location {weecfg.default_weewx_root} will be used.""" -CREATE_DESCRIPTION = """Create a new station data area, including a configuration file. """ \ - + WEEWX_ROOT_DESCRIPTION +RECONFIGURE_DESCRIPTION = f"""Reconfigure an existing configuration file at the location given +by the --config option. If the option is not provided, the location {weecfg.default_config_path} +will be used. Unless the --no-prompt option has been specified, the user will be prompted for +new values.""" -UPGRADE_DESCRIPTION = """Upgrade an existing station data area, including any combination of the -examples, utility files, configuration file, and skins. """ + WEEWX_ROOT_DESCRIPTION +UPGRADE_DESCRIPTION = f"""Upgrade an existing station data area managed by the configuration file +given by the --config option. If the option is not provided, the location +{weecfg.default_config_path} will be used. Any combination of the examples, utility files, +configuration file, and skins can be upgraded, """ def add_subparser(subparsers): @@ -86,59 +89,143 @@ def add_subparser(subparsers): title='Which action to take') # ---------- Action 'create' ---------- - station_create_parser = action_parser.add_parser('create', - description=CREATE_DESCRIPTION, - usage=station_create_usage, - help='Create a new station data area, ' - 'including a configuration file.') - _add_common_args(station_create_parser) - station_create_parser.add_argument('--user-root', - metavar='DIRECTORY', - help='Where to put the "user" directory, relative to ' - 'WEEWX_ROOT. Default is "bin/user"') - station_create_parser.add_argument('--examples-root', - metavar='DIRECTORY', - help='Where to put the examples, relative to ' - 'WEEWX_ROOT. Default is "examples".') - station_create_parser.add_argument('--no-prompt', action='store_true', - help='Do not prompt. Use default values.') - station_create_parser.add_argument('--config', - metavar='FILENAME', - help=f'Path to configuration file. It must not already ' - f'exist. Default is "{weecfg.default_config_path}".') - station_create_parser.add_argument('--dist-config', - metavar='FILENAME', - help='Use configuration file DIST-CONFIG-PATH as the ' - 'new configuration file. Default is to retrieve it ' - 'from package resources. The average user is ' - 'unlikely to need this option.') - station_create_parser.add_argument('--dry-run', - action='store_true', - help='Print what would happen, but do not actually ' - 'do it.') - station_create_parser.set_defaults(func=create_station) + create_parser = action_parser.add_parser('create', + description=CREATE_DESCRIPTION, + usage=station_create_usage, + help='Create a new station data area, including a ' + 'configuration file.') + create_parser.add_argument('--driver', + help='Driver to use. Default is "weewx.drivers.simulator".') + create_parser.add_argument('--location', + help='A description of the station. This will be used for report ' + 'titles. Default is "WeeWX".') + create_parser.add_argument('--altitude', metavar='ALTITUDE,{foot|meter}', + help='The station altitude in either feet or meters. For example, ' + '"750,foot" or "320,meter". Default is "0, foot".') + create_parser.add_argument('--latitude', + help='The station latitude in decimal degrees. Default is "0.00".') + create_parser.add_argument('--longitude', + help='The station longitude in decimal degrees. Default is "0.00".') + create_parser.add_argument('--register', choices=['y', 'n'], + help='Register this station in the weewx registry? Default is "n" ' + '(do not register).') + create_parser.add_argument('--station-url', + metavar='URL', + help='Unique URL to be used if registering the station. Required ' + 'if the station is to be registered.') + create_parser.add_argument('--units', choices=['us', 'metricwx', 'metric'], + dest='unit_system', + help='Set display units to us, metricwx, or metric. ' + 'Default is "us".') + create_parser.add_argument('--skin-root', + metavar='DIRECTORY', + help='Where to put the skins, relatve to WEEWX_ROOT. ' + 'Default is "skins".') + create_parser.add_argument('--sqlite-root', + metavar='DIRECTORY', + help='Where to put the SQLite database, relative to WEEWX_ROOT. ' + 'Default is "archive".') + create_parser.add_argument('--html-root', + metavar='DIRECTORY', + help='Where to put the generated HTML and images, relative to ' + 'WEEWX_ROOT. Default is "public_html".') + create_parser.add_argument('--user-root', + metavar='DIRECTORY', + help='Where to put the user extensions, relative to WEEWX_ROOT. ' + 'Default is "bin/user".') + create_parser.add_argument('--examples-root', + metavar='DIRECTORY', + help='Where to put the examples, relative to WEEWX_ROOT. ' + 'Default is "examples".') + create_parser.add_argument('--no-prompt', action='store_true', + help='Do not prompt. Use default values.') + create_parser.add_argument('--config', + metavar='FILENAME', + help='Where to put the configuration file, relative to WEEWX-ROOT. ' + 'It must not already exist. Default is "./weewx.conf".') + create_parser.add_argument('--dist-config', + metavar='FILENAME', + help='Use configuration file DIST-CONFIG-PATH as the new ' + 'configuration file. Default is to retrieve it from package ' + 'resources. The average user is unlikely to need this option.') + create_parser.add_argument('--dry-run', + action='store_true', + help='Print what would happen, but do not actually do it.') + create_parser.add_argument('weewx_root', + nargs='?', + metavar='WEEWX_ROOT', + help='Path to the WeeWX station data area to be created. ' + f'Default is {weecfg.default_weewx_root}.') + create_parser.set_defaults(func=create_station) # ---------- Action 'reconfigure' ---------- - station_reconfigure_parser = \ + reconfigure_parser = \ action_parser.add_parser('reconfigure', + description=RECONFIGURE_DESCRIPTION, usage=station_reconfigure_usage, - help='Reconfigure a station configuration file.') + help='Reconfigure an existing station configuration file.') - _add_common_args(station_reconfigure_parser) - station_reconfigure_parser.add_argument('--no-backup', action='store_true', - help='Do not backup the old configuration file.') - station_reconfigure_parser.add_argument('--no-prompt', action='store_true', - help='Do not prompt. Use default values.') - station_reconfigure_parser.add_argument('--config', - metavar='FILENAME', - help=f'Path to configuration file. ' - f'Default is "{weecfg.default_config_path}"') - station_reconfigure_parser.add_argument('--dry-run', - action='store_true', - help='Print what would happen, but do not actually ' - 'do it.') - station_reconfigure_parser.set_defaults(func=weectllib.dispatch) - station_reconfigure_parser.set_defaults(action_func=reconfigure_station) + reconfigure_parser.add_argument('--driver', + help='New driver to use. Default is the old driver.') + reconfigure_parser.add_argument('--location', + help='A new description for the station. This will be used ' + 'for report titles. Default is the old description.') + reconfigure_parser.add_argument('--altitude', metavar='ALTITUDE,{foot|meter}', + help='The new station altitude in either feet or meters. ' + 'For example, "750,foot" or "320,meter". ' + 'Default is the old altitude.') + reconfigure_parser.add_argument('--latitude', + help='The new station latitude in decimal degrees. ' + 'Default is the old latitude.') + reconfigure_parser.add_argument('--longitude', + help='The new station longitude in decimal degrees. ' + 'Default is the old longitude.') + reconfigure_parser.add_argument('--register', choices=['y', 'n'], + help='Register this station in the weewx registry? ' + 'Default is the old value.') + reconfigure_parser.add_argument('--station-url', + metavar='URL', + help='A new unique URL to be used if registering the . ' + 'station. Default is the old URL.') + reconfigure_parser.add_argument('--units', choices=['us', 'metricwx', 'metric'], + dest='unit_system', + help='New display units. Set to to us, metricwx, or metric. ' + 'Default is the old unit system.') + reconfigure_parser.add_argument('--skin-root', + metavar='DIRECTORY', + help='New location where to find the skins, relatve ' + 'to WEEWX_ROOT. Default is the old location.') + reconfigure_parser.add_argument('--sqlite-root', + metavar='DIRECTORY', + help='New location where to find the SQLite database, ' + 'relative to WEEWX_ROOT. Default is the old location.') + reconfigure_parser.add_argument('--html-root', + metavar='DIRECTORY', + help='New location where to put the generated HTML and ' + 'images, relative to WEEWX_ROOT. ' + 'Default is the old location.') + reconfigure_parser.add_argument('--user-root', + metavar='DIRECTORY', + help='New location where to find the user extensions, ' + 'relative to WEEWX_ROOT. Default is the old location.') + reconfigure_parser.add_argument('--weewx-root', + metavar='WEEWX_ROOT', + help='New path to the WeeWX station data area. ' + 'Default is the old path.') + reconfigure_parser.add_argument('--no-backup', action='store_true', + help='Do not backup the old configuration file.') + reconfigure_parser.add_argument('--no-prompt', action='store_true', + help='Do not prompt. Use default values.') + reconfigure_parser.add_argument('--config', + metavar='FILENAME', + help=f'Path to configuration file. ' + f'Default is "{weecfg.default_config_path}"') + reconfigure_parser.add_argument('--dry-run', + action='store_true', + help='Print what would happen, but do not actually ' + 'do it.') + reconfigure_parser.set_defaults(func=weectllib.dispatch) + reconfigure_parser.set_defaults(action_func=reconfigure_station) # ---------- Action 'upgrade' ---------- station_upgrade_parser = \ @@ -195,26 +282,27 @@ def add_subparser(subparsers): def create_station(namespace): """Map 'namespace' to a call to station_create()""" try: - config_dict \ - = weectllib.station_actions.station_create(config_path=namespace.config, - dist_config_path=namespace.dist_config, - driver=namespace.driver, - location=namespace.location, - altitude=namespace.altitude, - latitude=namespace.latitude, - longitude=namespace.longitude, - register=namespace.register, - station_url=namespace.station_url, - unit_system=namespace.unit_system, - weewx_root=namespace.weewx_root, - skin_root=namespace.skin_root, - sqlite_root=namespace.sqlite_root, - html_root=namespace.html_root, - examples_root=namespace.examples_root, - no_prompt=namespace.no_prompt, - dry_run=namespace.dry_run) + config_dict = weectllib.station_actions.station_create( + weewx_root=namespace.weewx_root, + rel_config_path=namespace.config, + driver=namespace.driver, + location=namespace.location, + altitude=namespace.altitude, + latitude=namespace.latitude, + longitude=namespace.longitude, + register=namespace.register, + station_url=namespace.station_url, + unit_system=namespace.unit_system, + skin_root=namespace.skin_root, + sqlite_root=namespace.sqlite_root, + html_root=namespace.html_root, + examples_root=namespace.examples_root, + user_root=namespace.user_root, + dist_config_path=namespace.dist_config, + no_prompt=namespace.no_prompt, + dry_run=namespace.dry_run) except weewx.ViolatedPrecondition as e: - sys.exit(e) + sys.exit(str(e)) # if this is an operating system for which we have a setup script, then # provide some guidance about running that script. @@ -253,6 +341,7 @@ def reconfigure_station(config_dict, namespace): skin_root=namespace.skin_root, sqlite_root=namespace.sqlite_root, html_root=namespace.html_root, + user_root=namespace.user_root, no_prompt=namespace.no_prompt, no_backup=namespace.no_backup, dry_run=namespace.dry_run) @@ -269,51 +358,3 @@ def upgrade_station(config_dict, namespace): no_prompt=namespace.no_prompt, no_backup=namespace.no_backup, dry_run=namespace.dry_run) - - -# ============================================================================== -# Utilities -# ============================================================================== - -def _add_common_args(parser): - """Add common arguments""" - parser.add_argument('--driver', - help='Driver to use. Default is "weewx.drivers.simulator".') - parser.add_argument('--location', - help='A description of the station. This will be used ' - 'for report titles. Default is "WeeWX".') - parser.add_argument('--altitude', metavar='ALTITUDE,{foot|meter}', - help='The station altitude in either feet or meters. ' - 'For example, "750,foot" or "320,meter". ' - 'Default is "0, foot".') - parser.add_argument('--latitude', - help='The station latitude in decimal degrees. ' - 'Default is "0.00".') - parser.add_argument('--longitude', - help='The station longitude in decimal degrees. ' - 'Default is "0.00".') - parser.add_argument('--register', choices=['y', 'n'], - help='Register this station in the weewx registry? ' - 'Default is "n" (do not register).') - parser.add_argument('--station-url', - metavar='URL', - help='Unique URL to be used if registering the station. ' - 'Required if the station is to be registered.') - parser.add_argument('--units', choices=['us', 'metricwx', 'metric'], - dest='unit_system', - help='Set display units to us, metricwx, or metric. ' - 'Default is "us".') - parser.add_argument('--weewx-root', - metavar='DIRECTORY', - help="Location of WEEWX_ROOT.") - parser.add_argument('--skin-root', - metavar='DIRECTORY', - help='Where to put the skins, relatve to WEEWX_ROOT. Default is "skins".') - parser.add_argument('--sqlite-root', - metavar='DIRECTORY', - help='Where to put the SQLite database, relative to WEEWX_ROOT. ' - 'Default is "archive".') - parser.add_argument('--html-root', - metavar='DIRECTORY', - help='Where to put the generated HTML and images, relative to WEEWX_ROOT. ' - 'Default is "public_html".') diff --git a/src/weectllib/tests/test_station_actions.py b/src/weectllib/tests/test_station_actions.py index 0d1ecb1a..c13c7740 100644 --- a/src/weectllib/tests/test_station_actions.py +++ b/src/weectllib/tests/test_station_actions.py @@ -295,29 +295,30 @@ class TestCreateStation(unittest.TestCase): def test_create_default(self): "Test creating a new station" # Get a temporary directory to create it in - with tempfile.TemporaryDirectory(dir='/var/tmp') as dirname: - config_path = os.path.join(dirname, 'weewx.conf') + with tempfile.TemporaryDirectory(dir='/var/tmp') as weewx_root: # We have not run 'pip', so the only copy of weewxd.py is the one in the repository. # Create a station using the defaults - weectllib.station_actions.station_create(config_path, no_prompt=True) + weectllib.station_actions.station_create(weewx_root=weewx_root, no_prompt=True) + config_path = os.path.join(weewx_root, 'weewx.conf') # Retrieve the config file that was created and check it: config_dict = configobj.ConfigObj(config_path, encoding='utf-8') - self.assertEqual(config_dict['WEEWX_ROOT'], dirname) + self.assertEqual(config_dict['WEEWX_ROOT'], './') + self.assertNotIn('WEEWX_ROOT_ORIG', config_dict) self.assertEqual(config_dict['Station']['station_type'], 'Simulator') self.assertEqual(config_dict['Simulator']['driver'], 'weewx.drivers.simulator') - self.assertEqual(config_dict['StdReport']['SKIN_ROOT'], 'skins') - self.assertEqual(config_dict['StdReport']['HTML_ROOT'], 'public_html') - self.assertEqual(config_dict['DatabaseTypes']['SQLite']['SQLITE_ROOT'], 'archive') + self.assertEqual(config_dict['StdReport']['SKIN_ROOT'], './skins') + self.assertEqual(config_dict['StdReport']['HTML_ROOT'], './public_html') + self.assertEqual(config_dict['DatabaseTypes']['SQLite']['SQLITE_ROOT'], './archive') # Make sure all the skins are there for skin in ['Seasons', 'Smartphone', 'Mobile', 'Standard', 'Ftp', 'Rsync']: - p = os.path.join(dirname, config_dict['StdReport']['SKIN_ROOT'], skin) + p = os.path.join(weewx_root, config_dict['StdReport']['SKIN_ROOT'], skin) self.assertTrue(os.path.isdir(p)) # Retrieve the systemd utility file and check it - path = os.path.join(dirname, 'util/systemd/weewx.service') + path = os.path.join(weewx_root, 'util/systemd/weewx.service') with open(path, 'rt') as fd: for line in fd: if line.startswith('ExecStart'): diff --git a/src/weewx_data/weewx.conf b/src/weewx_data/weewx.conf index f7bbced5..896378b1 100644 --- a/src/weewx_data/weewx.conf +++ b/src/weewx_data/weewx.conf @@ -10,7 +10,7 @@ # Set to 1 for extra debug info, otherwise comment it out or set to zero debug = 0 -# Root directory of the weewx data file hierarchy for this station +# Root directory of the weewx station data area, relative to this file. WEEWX_ROOT = ./ # Whether to log successful operations. May get overridden below.