Skip to content

Commit 25240c6

Browse files
committed
changed way that this_thread is accessed to avoid any issues with it being redefined after being imported; added allow external auth to bypass local auth; downgraded contourpy so that it would not make 3.11 the minimum Python version; upgraded packages as suggested by Dependabot; upgraded numpy because version was yanked
1 parent d0bb499 commit 25240c6

File tree

10 files changed

+172
-133
lines changed

10 files changed

+172
-133
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Change Log
22

3+
## [1.9.2] - 2026-01-27
4+
5+
### Added
6+
- The `allow external auth to bypass local auth` Configuration
7+
directive.
8+
9+
### Changed
10+
- The `.bates_number()` method of `DAFile` now accepts parameters
11+
`offset_horizontal`, `offset_vertical`, and `font_size`.
12+
- Upgraded `azure-core`, `wheel`, `numpy`, and `pyasn1`.
13+
- Downgraded `contourpy` to avoid requiring Python 3.11.
14+
315
## [1.9.1] - 2026-01-11
416

517
### Fixed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ bash -c \
5353
&& python3 -m venv --copies /usr/share/docassemble/local3.12 \
5454
&& source /usr/share/docassemble/local3.12/bin/activate \
5555
&& pip install --upgrade pip==25.3 \
56-
&& pip install --upgrade wheel==0.45.1 \
56+
&& pip install --upgrade wheel==0.46.2 \
5757
&& pip install --upgrade mod_wsgi==5.0.2 \
5858
&& pip install --upgrade \
5959
acme==5.2.2 \
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
metadata:
2+
title: Numbers
3+
documentation: "https://docassemble.org/docs/fields.html#number"
4+
---
5+
features:
6+
numeric inputs: True
7+
---
8+
question: |
9+
Describe your possessions.
10+
fields:
11+
- Number of cars: number_cars
12+
datatype: integer
13+
- Ounces of gold: gold_ounces
14+
datatype: number
15+
---
16+
question: Result of question
17+
subquestion: |
18+
% if int(number_cars) == 1:
19+
You have only one car.
20+
% else:
21+
You have ${ number_cars } cars.
22+
% endif
23+
You also have ${ gold_ounces }
24+
ounces of gold.
25+
mandatory: True

docassemble_base/docassemble/base/file_docx.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
from docx.oxml.table import CT_Tbl
1919
import docx
2020
from docxcompose.composer import Composer # For fixing up images, etc when including docx files within templates
21-
from docassemble.base.functions import server, this_thread, package_template_filename, get_config, roman
21+
import docassemble.base.functions
22+
from docassemble.base.functions import server, package_template_filename, get_config, roman
2223
from docassemble.base.error import DAError
2324
import docassemble.base.filter
2425
import docassemble.base.pandoc
@@ -135,13 +136,13 @@ def fix_subdoc(masterdoc, subdoc_info):
135136
def include_docx_template(template_file, **kwargs):
136137
"""Include the contents of one docx file inside another docx file."""
137138
use_jinja = kwargs.pop('_use_jinja2', True)
138-
if this_thread.evaluation_context is None:
139+
if docassemble.base.functions.this_thread.evaluation_context is None:
139140
return 'ERROR: not in a docx file'
140141
if template_file.__class__.__name__ in ('DAFile', 'DAFileList', 'DAFileCollection', 'DALocalFile', 'DAStaticFile'):
141142
template_path = template_file.path()
142143
else:
143-
template_path = package_template_filename(template_file, package=this_thread.current_package)
144-
sd = this_thread.misc['docx_template'].new_subdoc()
144+
template_path = package_template_filename(template_file, package=docassemble.base.functions.this_thread.current_package)
145+
sd = docassemble.base.functions.this_thread.misc['docx_template'].new_subdoc()
145146
sd.subdocx = docx.Document(template_path)
146147
change_numbering = bool(kwargs.pop('change_numbering', True))
147148
if '_inline' in kwargs:
@@ -153,12 +154,12 @@ def include_docx_template(template_file, **kwargs):
153154
# We need to keep a copy of the subdocs so we can fix up the master template in the end (in parse.py)
154155
# Given we're half way through processing the template, we can't fix the master template here
155156
# we have to do it in post
156-
if 'docx_subdocs' not in this_thread.misc:
157-
this_thread.misc['docx_subdocs'] = []
158-
this_thread.misc['docx_subdocs'].append({'subdoc': deepcopy(sd.subdocx), 'change_numbering': change_numbering})
157+
if 'docx_subdocs' not in docassemble.base.functions.this_thread.misc:
158+
docassemble.base.functions.this_thread.misc['docx_subdocs'] = []
159+
docassemble.base.functions.this_thread.misc['docx_subdocs'].append({'subdoc': deepcopy(sd.subdocx), 'change_numbering': change_numbering})
159160

160161
# Fix the subdocs before they are included in the template
161-
fix_subdoc(this_thread.misc['docx_template'], {'subdoc': sd.subdocx, 'change_numbering': change_numbering})
162+
fix_subdoc(docassemble.base.functions.this_thread.misc['docx_template'], {'subdoc': sd.subdocx, 'change_numbering': change_numbering})
162163

163164
first_paragraph = sd.subdocx.paragraphs[0]
164165

@@ -173,9 +174,9 @@ def include_docx_template(template_file, **kwargs):
173174
else:
174175
the_repr = '_codecs.decode(_array.array("b", "' + re.sub(r'\n', '', codecs.encode(bytearray(val, encoding='utf-8'), 'base64').decode()) + '".encode()), "base64").decode()'
175176
first_paragraph.insert_paragraph_before(str("{%%p set %s = %s %%}" % (key, the_repr)))
176-
if 'docx_include_count' not in this_thread.misc:
177-
this_thread.misc['docx_include_count'] = 0
178-
this_thread.misc['docx_include_count'] += 1
177+
if 'docx_include_count' not in docassemble.base.functions.this_thread.misc:
178+
docassemble.base.functions.this_thread.misc['docx_include_count'] = 0
179+
docassemble.base.functions.this_thread.misc['docx_include_count'] += 1
179180
if single_paragraph:
180181
return re.sub(r'<w:p[^>]*>\s*(.*)</w:p>\s*', r'\1', str(first_paragraph._p.xml), flags=re.DOTALL)
181182
return sd

docassemble_base/docassemble/base/sql.py

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import json
55
import sqlalchemy
66
from docassemble.base.logger import logmessage
7-
from docassemble.base.functions import server, this_thread
7+
import docassemble.base.functions
8+
from docassemble.base.functions import server
89
from docassemble.base.util import DAList, DAObjectPlusParameters
910
from docassemble.base.error import DAAttributeError, DAException
1011
from alembic.config import Config
@@ -72,17 +73,17 @@ def __setstate__(self, pickle_dict):
7273

7374
@classmethod
7475
def filter(cls, instance_name, **kwargs):
75-
if 'dbcache' not in this_thread.misc:
76-
this_thread.misc['dbcache'] = {}
76+
if 'dbcache' not in docassemble.base.functions.this_thread.misc:
77+
docassemble.base.functions.this_thread.misc['dbcache'] = {}
7778
listobj = DAList(instance_name, object_type=cls, auto_gather=False)
7879
filters = []
7980
for key, val in kwargs.items():
8081
if not hasattr(cls._model, key):
8182
raise DAException("filter: class " + cls.__name__ + " does not have column " + key)
8283
filters.append(getattr(cls._model, key) == val)
8384
for db_entry in list(cls._session.query(cls._model).filter(*filters).order_by(cls._model.id).all()):
84-
if cls._model.__name__ in this_thread.misc['dbcache'] and db_entry.id in this_thread.misc['dbcache'][cls._model.__name__]:
85-
listobj.append(this_thread.misc['dbcache'][cls._model.__name__][db_entry.id])
85+
if cls._model.__name__ in docassemble.base.functions.this_thread.misc['dbcache'] and db_entry.id in docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__]:
86+
listobj.append(docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__][db_entry.id])
8687
else:
8788
obj = listobj.appendObject()
8889
obj.id = db_entry.id
@@ -107,16 +108,16 @@ def any(cls):
107108

108109
@classmethod
109110
def all(cls, instance_name=None):
110-
if 'dbcache' not in this_thread.misc:
111-
this_thread.misc['dbcache'] = {}
111+
if 'dbcache' not in docassemble.base.functions.this_thread.misc:
112+
docassemble.base.functions.this_thread.misc['dbcache'] = {}
112113
if instance_name:
113114
listobj = DAList(instance_name, object_type=cls)
114115
else:
115116
listobj = DAList(object_type=cls)
116117
listobj.set_random_instance_name()
117118
for db_entry in list(cls._session.query(cls._model).order_by(cls._model.id).all()):
118-
if cls._model.__name__ in this_thread.misc['dbcache'] and db_entry.id in this_thread.misc['dbcache'][cls._model.__name__]:
119-
listobj.append(this_thread.misc['dbcache'][cls._model.__name__][db_entry.id])
119+
if cls._model.__name__ in docassemble.base.functions.this_thread.misc['dbcache'] and db_entry.id in docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__]:
120+
listobj.append(docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__][db_entry.id])
120121
else:
121122
obj = listobj.appendObject()
122123
obj.id = db_entry.id
@@ -134,12 +135,12 @@ def all(cls, instance_name=None):
134135

135136
@classmethod
136137
def by_id(cls, the_id, instance_name=None):
137-
if 'dbcache' not in this_thread.misc:
138-
this_thread.misc['dbcache'] = {}
139-
if cls._model.__name__ in this_thread.misc['dbcache'] and the_id in this_thread.misc['dbcache'][cls._model.__name__]:
138+
if 'dbcache' not in docassemble.base.functions.this_thread.misc:
139+
docassemble.base.functions.this_thread.misc['dbcache'] = {}
140+
if cls._model.__name__ in docassemble.base.functions.this_thread.misc['dbcache'] and the_id in docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__]:
140141
if instance_name is None:
141-
return this_thread.misc['dbcache'][cls._model.__name__][the_id]
142-
obj = this_thread.misc['dbcache'][cls._model.__name__][the_id]
142+
return docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__][the_id]
143+
obj = docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__][the_id]
143144
obj.fix_instance_name(obj.instanceName, instance_name)
144145
if instance_name is None:
145146
obj = cls(id=the_id)
@@ -169,8 +170,8 @@ def by_uid(cls, uid, instance_name=None):
169170
def delete_by_id(cls, the_id):
170171
cls._session.query(cls._model).filter(cls._model.id == the_id).delete()
171172
cls._session.commit()
172-
if 'dbcache' in this_thread.misc and cls._model.__name__ in this_thread.misc['dbcache'] and the_id in this_thread.misc['dbcache'][cls._model.__name__]:
173-
this_thread.misc['dbcache'][cls._model.__name__][the_id]._zombie = True
173+
if 'dbcache' in docassemble.base.functions.this_thread.misc and cls._model.__name__ in docassemble.base.functions.this_thread.misc['dbcache'] and the_id in docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__]:
174+
docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__][the_id]._zombie = True
174175

175176
@classmethod
176177
def delete_by_uid(cls, uid):
@@ -181,8 +182,8 @@ def delete_by_uid(cls, uid):
181182
the_id = db_entry.id
182183
cls._session.query(cls._model).filter(getattr(cls._model, cls._uid) == uid).delete()
183184
cls._session.commit()
184-
if 'dbcache' in this_thread.misc and cls._model.__name__ in this_thread.misc['dbcache'] and the_id in this_thread.misc['dbcache'][cls._model.__name__]:
185-
this_thread.misc['dbcache'][cls._model.__name__][the_id]._zombie = True
185+
if 'dbcache' in docassemble.base.functions.this_thread.misc and cls._model.__name__ in docassemble.base.functions.this_thread.misc['dbcache'] and the_id in docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__]:
186+
docassemble.base.functions.this_thread.misc['dbcache'][cls._model.__name__][the_id]._zombie = True
186187

187188
@classmethod
188189
def id_exists(cls, the_id):
@@ -201,25 +202,25 @@ def uid_exists(cls, uid):
201202
return True
202203

203204
def db_from_cache(self, the_id):
204-
if 'dbcache' not in this_thread.misc:
205-
this_thread.misc['dbcache'] = {}
206-
if self._model.__name__ not in this_thread.misc['dbcache']:
207-
this_thread.misc['dbcache'][self._model.__name__] = {}
208-
if the_id in this_thread.misc['dbcache'][self._model.__name__]:
209-
return this_thread.misc['dbcache'][self._model.__name__][the_id]
205+
if 'dbcache' not in docassemble.base.functions.this_thread.misc:
206+
docassemble.base.functions.this_thread.misc['dbcache'] = {}
207+
if self._model.__name__ not in docassemble.base.functions.this_thread.misc['dbcache']:
208+
docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__] = {}
209+
if the_id in docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__]:
210+
return docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__][the_id]
210211
return None
211212

212213
def db_cache(self):
213-
if 'dbcache' not in this_thread.misc:
214-
this_thread.misc['dbcache'] = {}
215-
if self._model.__name__ not in this_thread.misc['dbcache']:
216-
this_thread.misc['dbcache'][self._model.__name__] = {}
214+
if 'dbcache' not in docassemble.base.functions.this_thread.misc:
215+
docassemble.base.functions.this_thread.misc['dbcache'] = {}
216+
if self._model.__name__ not in docassemble.base.functions.this_thread.misc['dbcache']:
217+
docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__] = {}
217218
if hasattr(self, 'id'):
218-
this_thread.misc['dbcache'][self._model.__name__][self.id] = self
219+
docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__][self.id] = self
219220

220221
def __del__(self):
221-
if hasattr(self, 'id') and 'dbcache' in this_thread.misc and self._model.__name__ in this_thread.misc['dbcache'] and self.id in this_thread.misc['dbcache'][self._model.__name__]:
222-
del this_thread.misc['dbcache'][self._model.__name__][self.id]
222+
if hasattr(self, 'id') and 'dbcache' in docassemble.base.functions.this_thread.misc and self._model.__name__ in docassemble.base.functions.this_thread.misc['dbcache'] and self.id in docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__]:
223+
del docassemble.base.functions.this_thread.misc['dbcache'][self._model.__name__][self.id]
223224

224225
def db_delete(self):
225226
self.db_read()

docassemble_base/docassemble/base/standardformatter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2427,7 +2427,7 @@ def as_html(status, debug, root, validation_rules, field_error, the_progress_bar
24272427
<div class="card-body">
24282428
<form aria-labelledby="daheadingOne" action=\"""" + root + """\" id="daemailform" class="form-horizontal" method="POST">
24292429
<input type="hidden" name="_question_name" value=""" + json.dumps(status.question.name, ensure_ascii=False) + """/>
2430-
<div class="da-form-group row"><label for="da_attachment_email_address" class="col-""" + daconfig['grid classes']['label width'] + """ col-form-label da-form-label datext-right">""" + word('E-mail address') + """</label><div class="col-""" + daconfig['grid classes']['field width'] + """"><input alt=""" + fix_double_quote(word("Input box")) + """ class="form-control" type="email" name="_attachment_email_address" id="da_attachment_email_address" value=""" + fix_double_quote(str(default_email)) + """/></div></div>"""
2430+
<div class="da-form-group row"><label for="da_attachment_email_address" class="col-""" + daconfig['grid classes']['label width'] + ' col-form-label da-form-label datext-right">' + word('E-mail address') + '</label><div class="col-' + daconfig['grid classes']['field width'] + '"><input alt=' + fix_double_quote(word("Input box")) + ' class="form-control" type="email" name="_attachment_email_address" id="da_attachment_email_address" value=' + fix_double_quote(str(default_email)) + '/></div></div>'
24312431
if editable_included:
24322432
if automatically_include_editable:
24332433
output += """

0 commit comments

Comments
 (0)