Skip to content

Commit c334a45

Browse files
committed
Use g and current_app proxies
This should make the code a little cleaner to use these proxy objects.
1 parent 10068e1 commit c334a45

File tree

9 files changed

+48
-55
lines changed

9 files changed

+48
-55
lines changed

changedetectionio/__main__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@
1919

2020
# Parent wrapper or OS sends us a SIGTERM/SIGINT, do everything required for a clean shutdown
2121
class SigShutdownHandler(object):
22-
def __init__(self, app):
22+
def __init__(self, app: Flask, datastore: store.ChangeDetectionStore):
2323
self.app = app
24+
self.datastore = datastore
2425
signal.signal(signal.SIGTERM, lambda _signum, _frame: self._signal_handler("SIGTERM"))
2526
signal.signal(signal.SIGINT, lambda _signum, _frame: self._signal_handler("SIGINT"))
2627

2728
def _signal_handler(self, signame):
2829
logger.critical(f'Shutdown: Got Signal - {signame}, Saving DB to disk and calling shutdown')
29-
datastore = self.app.config["DATASTORE"]
30-
datastore.sync_to_json()
30+
self.datastore.sync_to_json()
3131
logger.success('Sync JSON to disk complete.')
3232
# This will throw a SystemExit exception, because eventlet.wsgi.server doesn't know how to deal with it.
3333
# Solution: move to gevent or other server in the future (#2014)
34-
datastore.stop_thread = True
34+
self.datastore.stop_thread = True
3535
self.app.config.exit.set()
3636
sys.exit(0)
3737

@@ -136,7 +136,7 @@ def create_application() -> Flask:
136136

137137
app = changedetection_app(app_config, datastore)
138138

139-
sigshutdown_handler = SigShutdownHandler(app)
139+
sigshutdown_handler = SigShutdownHandler(app, datastore)
140140

141141
# Go into cleanup mode
142142
if do_cleanup:

changedetectionio/flask_app.py

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from flask import (
2323
Flask,
2424
abort,
25+
current_app,
2526
flash,
2627
g,
2728
make_response,
@@ -133,17 +134,17 @@ def decorated_view(*args, **kwargs):
133134
has_password_enabled = g.datastore.data['settings']['application'].get('password') or os.getenv("SALTED_PASS", False)
134135

135136
# Permitted
136-
if request.endpoint == 'static_content' and request.view_args['group'] == 'styles':
137+
if request.endpoint == 'static_content' and request.view_args and request.view_args['group'] == 'styles':
137138
return func(*args, **kwargs)
138139
# Permitted
139140
elif request.endpoint == 'diff_history_page' and g.datastore.data['settings']['application'].get('shared_diff_access'):
140141
return func(*args, **kwargs)
141142
elif request.method in flask_login.config.EXEMPT_METHODS:
142143
return func(*args, **kwargs)
143-
elif g.app.config.get('LOGIN_DISABLED'):
144+
elif current_app.config.get('LOGIN_DISABLED'):
144145
return func(*args, **kwargs)
145146
elif has_password_enabled and not current_user.is_authenticated:
146-
return g.app.login_manager.unauthorized()
147+
return current_app.login_manager.unauthorized()
147148

148149
return func(*args, **kwargs)
149150

@@ -165,7 +166,8 @@ def changedetection_app(config, datastore):
165166

166167
# Stop browser caching of assets
167168
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
168-
app.config.exit = Event()
169+
exit_event = Event()
170+
app.config.exit = exit_event
169171

170172
app.config['NEW_VERSION_AVAILABLE'] = False
171173

@@ -182,8 +184,6 @@ def changedetection_app(config, datastore):
182184

183185
app.config["notification_debug_log"] = []
184186

185-
app.config['DATASTORE'] = datastore
186-
187187
login_manager = flask_login.LoginManager(app)
188188
login_manager.login_view = 'login'
189189
app.secret_key = init_app_secret(config['datastore_path'])
@@ -322,7 +322,6 @@ def login():
322322

323323
@app.before_request
324324
def remember_app_and_datastore():
325-
g.app = app
326325
g.datastore = datastore
327326

328327
@app.before_request
@@ -1630,25 +1629,23 @@ def highlight_submit_ignore_url():
16301629

16311630

16321631
# @todo handle ctrl break
1633-
threading.Thread(target=ticker_thread_check_time_launch_checks, args=(app,)).start()
1634-
threading.Thread(target=notification_runner, args=(app,)).start()
1632+
threading.Thread(target=ticker_thread_check_time_launch_checks, kwargs={'app': app, 'datastore': datastore, 'exit_event': exit_event}).start()
1633+
threading.Thread(target=notification_runner, kwargs={'app': app, 'datastore': datastore, 'exit_event': exit_event}).start()
16351634

16361635
# Check for new release version, but not when running in test/build or pytest
16371636
if not os.getenv("GITHUB_REF", False) and not strtobool(os.getenv('DISABLE_VERSION_CHECK', 'no')):
1638-
threading.Thread(target=check_for_new_version, args=(app,)).start()
1637+
threading.Thread(target=check_for_new_version, kwargs={'app': app, 'datastore': datastore, 'exit_event': exit_event}).start()
16391638

16401639
return app
16411640

16421641

16431642
# Check for new version and anonymous stats
1644-
def check_for_new_version(app, url="https://changedetection.io/check-ver.php", delay_time=86400):
1643+
def check_for_new_version(*, app, datastore, exit_event, url="https://changedetection.io/check-ver.php", delay_time=86400):
16451644
import requests
16461645
import urllib3
16471646
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
16481647

1649-
datastore = app.config["DATASTORE"]
1650-
1651-
while not app.config.exit.is_set():
1648+
while not exit_event.is_set():
16521649
try:
16531650
r = requests.post(url,
16541651
data={'version': __version__,
@@ -1667,13 +1664,13 @@ def check_for_new_version(app, url="https://changedetection.io/check-ver.php", d
16671664
pass
16681665

16691666
# Check daily
1670-
app.config.exit.wait(delay_time)
1667+
exit_event.wait(delay_time)
16711668

16721669

1673-
def notification_runner(app):
1670+
def notification_runner(*, app, datastore, exit_event):
16741671
from datetime import datetime
16751672
import json
1676-
while not app.config.exit.is_set():
1673+
while not exit_event.is_set():
16771674
try:
16781675
# At the moment only one thread runs (single runner)
16791676
n_object = notification_q.get(block=False)
@@ -1687,8 +1684,6 @@ def notification_runner(app):
16871684

16881685
notification_debug_log = app.config["notification_debug_log"]
16891686

1690-
datastore = app.config["DATASTORE"]
1691-
16921687
try:
16931688
from changedetectionio import notification
16941689
# Fallback to system config if not set
@@ -1720,12 +1715,10 @@ def notification_runner(app):
17201715
notification_debug_log = notification_debug_log[-100:]
17211716

17221717
# Threaded runner, look for new watches to feed into the Queue.
1723-
def ticker_thread_check_time_launch_checks(app):
1718+
def ticker_thread_check_time_launch_checks(*, app, datastore, exit_event):
17241719
import random
17251720
from changedetectionio import update_worker
17261721

1727-
datastore = app.config["DATASTORE"]
1728-
17291722
proxy_last_called_time = {}
17301723

17311724
recheck_time_minimum_seconds = int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 3))
@@ -1739,7 +1732,7 @@ def ticker_thread_check_time_launch_checks(app):
17391732
running_update_threads.append(new_worker)
17401733
new_worker.start()
17411734

1742-
while not app.config.exit.is_set():
1735+
while not exit_event.is_set():
17431736

17441737
# Get a list of watches by UUID that are currently fetching data
17451738
running_uuids = []
@@ -1835,4 +1828,4 @@ def ticker_thread_check_time_launch_checks(app):
18351828
time.sleep(1)
18361829

18371830
# Should be low so we can break this out in testing
1838-
app.config.exit.wait(1)
1831+
exit_event.wait(1)

changedetectionio/tests/fetchers/test_custom_js_before_content.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from flask import url_for
2+
from flask import url_for, g
33
from ..util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
44

55

@@ -35,7 +35,7 @@ def test_execute_custom_js(client, live_server, measure_memory_usage):
3535
wait_for_all_checks(client)
3636

3737
uuid = extract_UUID_from_client(client)
38-
assert live_server.app.config['DATASTORE'].data['watching'][uuid].history_n >= 1, "Watch history had atleast 1 (everything fetched OK)"
38+
assert g.datastore.data['watching'][uuid].history_n >= 1, "Watch history had atleast 1 (everything fetched OK)"
3939

4040
assert b"This text should be removed" not in res.data
4141

@@ -53,4 +53,4 @@ def test_execute_custom_js(client, live_server, measure_memory_usage):
5353
client.get(
5454
url_for("form_delete", uuid="all"),
5555
follow_redirects=True
56-
)
56+
)

changedetectionio/tests/proxy_list/test_noproxy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22

33
import time
4-
from flask import url_for
4+
from flask import url_for, g
55
from ..util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
66

77

@@ -73,5 +73,5 @@ def test_noproxy_option(client, live_server, measure_memory_usage):
7373

7474
# Prove that it actually checked
7575

76-
assert live_server.app.config['DATASTORE'].data['watching'][uuid]['last_checked'] != 0
76+
assert g.datastore.data['watching'][uuid]['last_checked'] != 0
7777

changedetectionio/tests/test_encoding.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# coding=utf-8
33

44
import time
5-
from flask import url_for
5+
from flask import url_for, g
66
from .util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
77
import pytest
88

@@ -41,7 +41,7 @@ def test_check_encoding_detection(client, live_server, measure_memory_usage):
4141

4242
# Content type recording worked
4343
uuid = extract_UUID_from_client(client)
44-
assert live_server.app.config['DATASTORE'].data['watching'][uuid]['content-type'] == "text/html"
44+
assert g.datastore.data['watching'][uuid]['content-type'] == "text/html"
4545

4646
res = client.get(
4747
url_for("preview_page", uuid="first"),

changedetectionio/tests/test_filter_failure_notification.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import time
33
from loguru import logger
4-
from flask import url_for
4+
from flask import url_for, g
55
from .util import set_original_response, live_server_setup, extract_UUID_from_client, wait_for_all_checks, \
66
wait_for_notification_endpoint_output
77
from changedetectionio.model import App
@@ -53,7 +53,7 @@ def run_filter_test(client, live_server, content_filter):
5353

5454
uuid = extract_UUID_from_client(client)
5555

56-
assert live_server.app.config['DATASTORE'].data['watching'][uuid]['consecutive_filter_failures'] == 0, "No filter = No filter failure"
56+
assert g.datastore.data['watching'][uuid]['consecutive_filter_failures'] == 0, "No filter = No filter failure"
5757

5858
watch_data = {"notification_urls": notification_url,
5959
"notification_title": "New ChangeDetection.io Notification - {{watch_url}}",
@@ -86,7 +86,7 @@ def run_filter_test(client, live_server, content_filter):
8686
)
8787
assert b"Updated watch." in res.data
8888
wait_for_all_checks(client)
89-
assert live_server.app.config['DATASTORE'].data['watching'][uuid]['consecutive_filter_failures'] == 0, "No filter = No filter failure"
89+
assert g.datastore.data['watching'][uuid]['consecutive_filter_failures'] == 0, "No filter = No filter failure"
9090

9191
# Now add a filter, because recheck hours == 5, ONLY pressing of the [edit] or [recheck all] should trigger
9292
watch_data['include_filters'] = content_filter
@@ -103,12 +103,12 @@ def run_filter_test(client, live_server, content_filter):
103103
assert not os.path.isfile("test-datastore/notification.txt")
104104

105105
# Hitting [save] would have triggered a recheck, and we have a filter, so this would be ONE failure
106-
assert live_server.app.config['DATASTORE'].data['watching'][uuid]['consecutive_filter_failures'] == 1, "Should have been checked once"
106+
assert g.datastore.data['watching'][uuid]['consecutive_filter_failures'] == 1, "Should have been checked once"
107107

108108
# recheck it up to just before the threshold, including the fact that in the previous POST it would have rechecked (and incremented)
109109
# Add 4 more checks
110110
checked = 0
111-
ATTEMPT_THRESHOLD_SETTING = live_server.app.config['DATASTORE'].data['settings']['application'].get('filter_failure_notification_threshold_attempts', 0)
111+
ATTEMPT_THRESHOLD_SETTING = g.datastore.data['settings']['application'].get('filter_failure_notification_threshold_attempts', 0)
112112
for i in range(0, ATTEMPT_THRESHOLD_SETTING - 2):
113113
checked += 1
114114
client.get(url_for("form_watch_checknow"), follow_redirects=True)
@@ -118,7 +118,7 @@ def run_filter_test(client, live_server, content_filter):
118118
assert not os.path.isfile("test-datastore/notification.txt")
119119
time.sleep(1)
120120

121-
assert live_server.app.config['DATASTORE'].data['watching'][uuid]['consecutive_filter_failures'] == 5
121+
assert g.datastore.data['watching'][uuid]['consecutive_filter_failures'] == 5
122122

123123
time.sleep(2)
124124
# One more check should trigger the _FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT threshold

changedetectionio/tests/test_history_consistency.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
import json
66
import logging
7-
from flask import url_for
7+
from flask import url_for, g
88
from .util import live_server_setup, wait_for_all_checks
99
from urllib.parse import urlparse, parse_qs
1010

@@ -38,7 +38,7 @@ def test_consistent_history(client, live_server, measure_memory_usage):
3838

3939
time.sleep(2)
4040

41-
json_db_file = os.path.join(live_server.app.config['DATASTORE'].datastore_path, 'url-watches.json')
41+
json_db_file = os.path.join(g.datastore.datastore_path, 'url-watches.json')
4242

4343
json_obj = None
4444
with open(json_db_file, 'r') as f:
@@ -49,7 +49,7 @@ def test_consistent_history(client, live_server, measure_memory_usage):
4949

5050
# each one should have a history.txt containing just one line
5151
for w in json_obj['watching'].keys():
52-
history_txt_index_file = os.path.join(live_server.app.config['DATASTORE'].datastore_path, w, 'history.txt')
52+
history_txt_index_file = os.path.join(g.datastore.datastore_path, w, 'history.txt')
5353
assert os.path.isfile(history_txt_index_file), f"History.txt should exist where I expect it at {history_txt_index_file}"
5454

5555
# Same like in model.Watch
@@ -58,13 +58,13 @@ def test_consistent_history(client, live_server, measure_memory_usage):
5858
assert len(tmp_history) == 1, "History.txt should contain 1 line"
5959

6060
# Should be two files,. the history.txt , and the snapshot.txt
61-
files_in_watch_dir = os.listdir(os.path.join(live_server.app.config['DATASTORE'].datastore_path,
61+
files_in_watch_dir = os.listdir(os.path.join(g.datastore.datastore_path,
6262
w))
6363
# Find the snapshot one
6464
for fname in files_in_watch_dir:
6565
if fname != 'history.txt' and 'html' not in fname:
6666
# contents should match what we requested as content returned from the test url
67-
with open(os.path.join(live_server.app.config['DATASTORE'].datastore_path, w, fname), 'r') as snapshot_f:
67+
with open(os.path.join(g.datastore.datastore_path, w, fname), 'r') as snapshot_f:
6868
contents = snapshot_f.read()
6969
watch_url = json_obj['watching'][w]['url']
7070
u = urlparse(watch_url)
@@ -76,6 +76,6 @@ def test_consistent_history(client, live_server, measure_memory_usage):
7676
assert len(files_in_watch_dir) == 3, "Should be just three files in the dir, html.br snapshot, history.txt and the extracted text snapshot"
7777

7878

79-
json_db_file = os.path.join(live_server.app.config['DATASTORE'].datastore_path, 'url-watches.json')
79+
json_db_file = os.path.join(g.datastore.datastore_path, 'url-watches.json')
8080
with open(json_db_file, 'r') as f:
8181
assert '"default"' not in f.read(), "'default' probably shouldnt be here, it came from when the 'default' Watch vars were accidently being saved"

changedetectionio/tests/test_import.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
import time
55

6-
from flask import url_for
6+
from flask import url_for, g
77

88
from .util import live_server_setup, wait_for_all_checks
99

@@ -163,7 +163,7 @@ def test_import_custom_xlsx(client, live_server, measure_memory_usage):
163163
assert b'City news results' in res.data
164164

165165
# Just find one to check over
166-
for uuid, watch in live_server.app.config['DATASTORE'].data['watching'].items():
166+
for uuid, watch in g.datastore.data['watching'].items():
167167
if watch.get('title') == 'Somesite results ABC':
168168
filters = watch.get('include_filters')
169169
assert filters[0] == '/html[1]/body[1]/div[4]/div[1]/div[1]/div[1]||//*[@id=\'content\']/div[3]/div[1]/div[1]||//*[@id=\'content\']/div[1]'
@@ -201,7 +201,7 @@ def test_import_watchete_xlsx(client, live_server, measure_memory_usage):
201201
assert b'City news results' in res.data
202202

203203
# Just find one to check over
204-
for uuid, watch in live_server.app.config['DATASTORE'].data['watching'].items():
204+
for uuid, watch in g.datastore.data['watching'].items():
205205
if watch.get('title') == 'Somesite results ABC':
206206
filters = watch.get('include_filters')
207207
assert filters[0] == '/html[1]/body[1]/div[4]/div[1]/div[1]/div[1]||//*[@id=\'content\']/div[3]/div[1]/div[1]||//*[@id=\'content\']/div[1]'

changedetectionio/tests/visualselector/test_fetch_data.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22

33
import os
4-
from flask import url_for
4+
from flask import url_for, g
55
from ..util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
66

77
def test_setup(client, live_server, measure_memory_usage):
@@ -43,7 +43,7 @@ def test_visual_selector_content_ready(client, live_server, measure_memory_usage
4343
wait_for_all_checks(client)
4444

4545

46-
assert live_server.app.config['DATASTORE'].data['watching'][uuid].history_n >= 1, "Watch history had atleast 1 (everything fetched OK)"
46+
assert g.datastore.data['watching'][uuid].history_n >= 1, "Watch history had atleast 1 (everything fetched OK)"
4747

4848
res = client.get(
4949
url_for("preview_page", uuid=uuid),
@@ -120,7 +120,7 @@ def test_basic_browserstep(client, live_server, measure_memory_usage):
120120
wait_for_all_checks(client)
121121

122122
uuid = extract_UUID_from_client(client)
123-
assert live_server.app.config['DATASTORE'].data['watching'][uuid].history_n >= 1, "Watch history had atleast 1 (everything fetched OK)"
123+
assert g.datastore.data['watching'][uuid].history_n >= 1, "Watch history had atleast 1 (everything fetched OK)"
124124

125125
assert b"This text should be removed" not in res.data
126126

@@ -161,4 +161,4 @@ def test_basic_browserstep(client, live_server, measure_memory_usage):
161161
client.get(
162162
url_for("form_delete", uuid="all"),
163163
follow_redirects=True
164-
)
164+
)

0 commit comments

Comments
 (0)