diff --git a/asteval/astutils.py b/asteval/astutils.py
index 4dab4fc..2052683 100644
--- a/asteval/astutils.py
+++ b/asteval/astutils.py
@@ -122,7 +122,7 @@
     'insert', 'int32', 'integer', 'interp', 'intersect1d', 'invert',
     'iscomplex', 'iscomplexobj', 'isfinite', 'isinf', 'isnan', 'isneginf',
     'isposinf', 'isreal', 'isrealobj', 'isscalar', 'iterable', 'kaiser',
-    'kron', 'ldexp', 'left_shift', 'less', 'less_equal', 'linalg', 'linspace',
+    'kron', 'ldexp', 'left_shift', 'less', 'less_equal', 'linspace',
     'little_endian', 'loadtxt', 'log', 'log10', 'log1p', 'log2', 'logaddexp',
     'logaddexp2', 'logical_and', 'logical_not', 'logical_or', 'logical_xor',
     'logspace', 'longdouble', 'longlong', 'mask_indices', 'matrix', 'maximum',
@@ -506,59 +506,59 @@ def __init__(self, name, interp, doc=None, lineno=0,
         self.name = name
         self.__name__ = self.name
         self.__asteval__ = interp
-        self.__raise_exc__ = self.__asteval__.raise_exception
+        self.raise_exc = self.__asteval__.raise_exception
         self.__doc__ = doc
-        self.__body__ = body
-        self.__argnames__ = args
-        self.__kwargs__ = kwargs
-        self.__vararg__ = vararg
-        self.__varkws__ = varkws
+        self.body = body
+        self.argnames = args
+        self.kwargs = kwargs
+        self.vararg = vararg
+        self.varkws = varkws
         self.lineno = lineno
         self.__ininit__ = False
 
     def __setattr__(self, attr, val):
         if not getattr(self, '__ininit__', True):
-            self.__raise_exc__(None, exc=TypeError,
+            self.raise_exc(None, exc=TypeError,
                            msg="procedure is read-only")
         self.__dict__[attr] = val
 
     def __dir__(self):
-        return ['__getdoc__', '__argnames__', 'kwargs', 'name', 'vararg', 'varkws']
+        return ['_getdoc', 'argnames', 'kwargs', 'name', 'vararg', 'varkws']
 
-    def __getdoc__(self):
+    def _getdoc(self):
         doc = self.__doc__
         if isinstance(doc, ast.Constant):
             doc = doc.value
         return doc
 
     def __repr__(self):
-        """Procedure repr"""
-        sig = self.__signature__()
+        """TODO: docstring in magic method."""
+        sig = self._signature()
         rep = f"<Procedure {sig}>"
-        doc = self.__getdoc__()
+        doc = self._getdoc()
         if doc is not None:
             rep = f"{rep}\n {doc}"
         return rep
 
-    def __signature__(self):
-        "return the procedure's call signature"
+    def _signature(self):
+        "call signature"
         sig = ""
-        if len(self.__argnames__) > 0:
-            sig = sig +  ', '.join(self.__argnames__)
-        if self.__vararg__ is not None:
-            sig = sig + f"*{self.__vararg__}"
-        if len(self.__kwargs__) > 0:
+        if len(self.argnames) > 0:
+            sig = sig +  ', '.join(self.argnames)
+        if self.vararg is not None:
+            sig = sig + f"*{self.vararg}"
+        if len(self.kwargs) > 0:
             if len(sig) > 0:
                 sig = f"{sig}, "
-            _kw = [f"{k}={v}" for k, v in self.__kwargs__]
+            _kw = [f"{k}={v}" for k, v in self.kwargs]
             sig = f"{sig}{', '.join(_kw)}"
 
-            if self.__varkws__ is not None:
-                sig = f"{sig}, **{self.__varkws__}"
+            if self.varkws is not None:
+                sig = f"{sig}, **{self.varkws}"
         return f"{self.name}({sig})"
 
     def __call__(self, *args, **kwargs):
-        """call the Procedure"""
+        """TODO: docstring in public method."""
         topsym = self.__asteval__.symtable
         if self.__asteval__.config.get('nested_symtable', False):
             sargs = {'_main': topsym}
@@ -576,27 +576,27 @@ def __call__(self, *args, **kwargs):
         args = list(args)
         nargs = len(args)
         nkws = len(kwargs)
-        nargs_expected = len(self.__argnames__)
+        nargs_expected = len(self.argnames)
 
         # check for too few arguments, but the correct keyword given
         if (nargs < nargs_expected) and nkws > 0:
-            for name in self.__argnames__[nargs:]:
+            for name in self.argnames[nargs:]:
                 if name in kwargs:
                     args.append(kwargs.pop(name))
             nargs = len(args)
-            nargs_expected = len(self.__argnames__)
+            nargs_expected = len(self.argnames)
             nkws = len(kwargs)
         if nargs < nargs_expected:
             msg = f"{self.name}() takes at least"
             msg = f"{msg} {nargs_expected} arguments, got {nargs}"
-            self.__raise_exc__(None, exc=TypeError, msg=msg)
+            self.raise_exc(None, exc=TypeError, msg=msg)
         # check for multiple values for named argument
-        if len(self.__argnames__) > 0 and kwargs is not None:
+        if len(self.argnames) > 0 and kwargs is not None:
             msg = "multiple values for keyword argument"
-            for targ in self.__argnames__:
+            for targ in self.argnames:
                 if targ in kwargs:
                     msg = f"{msg} '{targ}' in Procedure {self.name}"
-                    self.__raise_exc__(None, exc=TypeError, msg=msg, lineno=self.lineno)
+                    self.raise_exc(None, exc=TypeError, msg=msg, lineno=self.lineno)
 
         # check more args given than expected, varargs not given
         if nargs != nargs_expected:
@@ -604,44 +604,44 @@ def __call__(self, *args, **kwargs):
             if nargs < nargs_expected:
                 msg = f"not enough arguments for Procedure {self.name}()"
                 msg = f"{msg} (expected {nargs_expected}, got {nargs}"
-                self.__raise_exc__(None, exc=TypeError, msg=msg)
+                self.raise_exc(None, exc=TypeError, msg=msg)
 
-        if nargs > nargs_expected and self.__vararg__ is None:
-            if nargs - nargs_expected > len(self.__kwargs__):
+        if nargs > nargs_expected and self.vararg is None:
+            if nargs - nargs_expected > len(self.kwargs):
                 msg = f"too many arguments for {self.name}() expected at most"
-                msg = f"{msg} {len(self.__kwargs__)+nargs_expected}, got {nargs}"
-                self.__raise_exc__(None, exc=TypeError, msg=msg)
+                msg = f"{msg} {len(self.kwargs)+nargs_expected}, got {nargs}"
+                self.raise_exc(None, exc=TypeError, msg=msg)
 
             for i, xarg in enumerate(args[nargs_expected:]):
-                kw_name = self.__kwargs__[i][0]
+                kw_name = self.kwargs[i][0]
                 if kw_name not in kwargs:
                     kwargs[kw_name] = xarg
 
-        for argname in self.__argnames__:
+        for argname in self.argnames:
             symlocals[argname] = args.pop(0)
 
         try:
-            if self.__vararg__ is not None:
-                symlocals[self.__vararg__] = tuple(args)
+            if self.vararg is not None:
+                symlocals[self.vararg] = tuple(args)
 
-            for key, val in self.__kwargs__:
+            for key, val in self.kwargs:
                 if key in kwargs:
                     val = kwargs.pop(key)
                 symlocals[key] = val
 
-            if self.__varkws__ is not None:
-                symlocals[self.__varkws__] = kwargs
+            if self.varkws is not None:
+                symlocals[self.varkws] = kwargs
 
             elif len(kwargs) > 0:
                 msg = f"extra keyword arguments for Procedure {self.name}: "
                 msg = msg + ','.join(list(kwargs.keys()))
-                self.__raise_exc__(None, msg=msg, exc=TypeError,
+                self.raise_exc(None, msg=msg, exc=TypeError,
                                lineno=self.lineno)
 
         except (ValueError, LookupError, TypeError,
                 NameError, AttributeError):
             msg = f"incorrect arguments for Procedure {self.name}"
-            self.__raise_exc__(None, msg=msg, lineno=self.lineno)
+            self.raise_exc(None, msg=msg, lineno=self.lineno)
 
         if self.__asteval__.config.get('nested_symtable', False):
             save_symtable = self.__asteval__.symtable
@@ -655,7 +655,7 @@ def __call__(self, *args, **kwargs):
         retval = None
 
         # evaluate script of function
-        for node in self.__body__:
+        for node in self.body:
             self.__asteval__.run(node, expr='<>', lineno=self.lineno)
             if len(self.__asteval__.error) > 0:
                 break
diff --git a/doc/Makefile b/doc/Makefile
index 5a777c0..695e2af 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -4,16 +4,11 @@
 # You can set these variables from the command line.
 SPHINXOPTS    =
 SPHINXBUILD   = sphinx-build
-PAPER         =
 BUILDDIR      = _build
-
 INSTALLDIR = /home/newville/public_html/asteval/
 
-
 # Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .
 
 .PHONY: html pdf all help clean dirhtml latex
 
@@ -31,6 +26,12 @@ html:
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 
+tarball: all
+	cd $(BUILDDIR)/html && tar cvzf ../../../_doc.tgz *
+	cd ../../
+	@echo
+
+
 pdf: latex
 	cd $(BUILDDIR)/latex && make all-pdf
 	cp -pr $(BUILDDIR)/latex/asteval.pdf ./asteval.pdf
diff --git a/doc/conf.py b/doc/conf.py
index b611cdd..b80dd0b 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -172,7 +172,7 @@
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+latex_papersize = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
 #latex_font_size = '10pt'
diff --git a/doc/motivation.rst b/doc/motivation.rst
index df4c051..c8160d6 100644
--- a/doc/motivation.rst
+++ b/doc/motivation.rst
@@ -108,10 +108,16 @@ needed, these modules can be added to any Interpreter either using the
 ``user_symbols`` argument when creating it, or adding the needed symbols to the
 symbol table after the Interpreter is created.
 
-There are important categories of safety that asteval may attempt to address,
-but cannot guarantee success.  The most important of these is resource hogging,
-which might be used for a denial-of-service attack.  There is no guaranteed
-timeout on any calculation, and so a reasonable looking calculation such as::
+In 2025, a security audit by William Khem Marquez showed a
+vulnerability from leaving some AST objects exposed within the
+interpreter for user-defined functions ("Procedures"), and this was
+fixed for version 1.0.6.
+
+There are other categories of safety that asteval may attempt to
+address, but cannot guarantee success.  The most important of these is
+resource hogging, which might be used for a denial-of-service attack.
+There is no guaranteed timeout on any calculation, and so a reasonable
+looking calculation such as::
 
    from asteval import Interpreter
    aeval = Interpreter()