aboutsummaryrefslogtreecommitdiffstats
path: root/pathod
diff options
context:
space:
mode:
Diffstat (limited to 'pathod')
-rw-r--r--pathod/app.py9
-rw-r--r--pathod/language/__init__.py15
-rw-r--r--pathod/language/base.py25
-rw-r--r--pathod/language/exceptions.py1
-rw-r--r--pathod/language/generators.py57
-rw-r--r--pathod/language/http.py1
-rw-r--r--pathod/language/http2.py7
-rw-r--r--pathod/language/websockets.py1
-rw-r--r--pathod/language/writer.py1
-rw-r--r--pathod/log.py12
-rw-r--r--pathod/pathoc.py19
-rw-r--r--pathod/pathod.py25
-rw-r--r--pathod/pathod_cmdline.py15
-rw-r--r--pathod/protocols/__init__.py6
-rw-r--r--pathod/protocols/http.py6
-rw-r--r--pathod/protocols/http2.py3
-rw-r--r--pathod/protocols/websockets.py2
-rw-r--r--pathod/test.py49
-rw-r--r--pathod/utils.py50
-rw-r--r--pathod/version.py7
20 files changed, 154 insertions, 157 deletions
diff --git a/pathod/app.py b/pathod/app.py
index aa00ed69..e3216c58 100644
--- a/pathod/app.py
+++ b/pathod/app.py
@@ -1,10 +1,11 @@
import logging
import pprint
-from six.moves import cStringIO as StringIO
+import io
import copy
from flask import Flask, jsonify, render_template, request, abort, make_response
-from . import version, language, utils
+from . import version, language
from netlib.http import user_agents
+from netlib import strutils
logging.basicConfig(level="DEBUG")
EXAMPLE_HOST = "example.com"
@@ -145,7 +146,7 @@ def make_app(noapi, debug):
args["marked"] = v.marked()
return render(template, False, **args)
- s = StringIO()
+ s = io.BytesIO()
settings = copy.copy(app.config["pathod"].settings)
settings.request_host = EXAMPLE_HOST
@@ -166,7 +167,7 @@ def make_app(noapi, debug):
settings.websocket_key = EXAMPLE_WEBSOCKET_KEY
language.serve(safe, s, settings)
- args["output"] = utils.escape_unprintables(s.getvalue())
+ args["output"] = strutils.bytes_to_escaped_str(s.getvalue())
return render(template, False, **args)
@app.route('/response_preview')
diff --git a/pathod/language/__init__.py b/pathod/language/__init__.py
index 32199e08..0841196e 100644
--- a/pathod/language/__init__.py
+++ b/pathod/language/__init__.py
@@ -1,19 +1,26 @@
+from __future__ import absolute_import
+
import itertools
import time
+from six.moves import range
import pyparsing as pp
from . import http, http2, websockets, writer, exceptions
-from exceptions import *
-from base import Settings
-assert Settings # prevent pyflakes from messing with this
+from .exceptions import RenderError, FileAccessDenied, ParseException
+from .base import Settings
+
+__all__ = [
+ "RenderError", "FileAccessDenied", "ParseException",
+ "Settings",
+]
def expand(msg):
times = getattr(msg, "times", None)
if times:
- for j_ in xrange(int(times.value)):
+ for j_ in range(int(times.value)):
yield msg.strike_token("times")
else:
yield msg
diff --git a/pathod/language/base.py b/pathod/language/base.py
index a4302998..11ee0623 100644
--- a/pathod/language/base.py
+++ b/pathod/language/base.py
@@ -3,9 +3,14 @@ import os
import abc
import pyparsing as pp
-from .. import utils
+import six
+from six.moves import reduce
+from netlib import strutils
+from netlib import human
+
from . import generators, exceptions
+
class Settings(object):
def __init__(
@@ -105,7 +110,7 @@ class Token(object):
class _TokValueLiteral(Token):
def __init__(self, val):
- self.val = val.decode("string_escape")
+ self.val = strutils.escaped_str_to_bytes(val)
def get_generator(self, settings_):
return self.val
@@ -130,7 +135,7 @@ class TokValueLiteral(_TokValueLiteral):
return v
def spec(self):
- inner = self.val.encode("string_escape")
+ inner = strutils.bytes_to_escaped_str(self.val)
inner = inner.replace(r"\'", r"\x27")
return "'" + inner + "'"
@@ -143,7 +148,7 @@ class TokValueNakedLiteral(_TokValueLiteral):
return e.setParseAction(lambda x: cls(*x))
def spec(self):
- return self.val.encode("string_escape")
+ return strutils.bytes_to_escaped_str(self.val)
class TokValueGenerate(Token):
@@ -154,14 +159,14 @@ class TokValueGenerate(Token):
self.usize, self.unit, self.datatype = usize, unit, datatype
def bytes(self):
- return self.usize * utils.SIZE_UNITS[self.unit]
+ return self.usize * human.SIZE_UNITS[self.unit]
def get_generator(self, settings_):
return generators.RandomGenerator(self.datatype, self.bytes())
def freeze(self, settings):
g = self.get_generator(settings)
- return TokValueLiteral(g[:].encode("string_escape"))
+ return TokValueLiteral(strutils.bytes_to_escaped_str(g[:]))
@classmethod
def expr(cls):
@@ -169,7 +174,7 @@ class TokValueGenerate(Token):
u = reduce(
operator.or_,
- [pp.Literal(i) for i in utils.SIZE_UNITS.keys()]
+ [pp.Literal(i) for i in human.SIZE_UNITS.keys()]
).leaveWhitespace()
e = e + pp.Optional(u, default=None)
@@ -221,7 +226,7 @@ class TokValueFile(Token):
return generators.FileGenerator(s)
def spec(self):
- return "<'%s'" % self.path.encode("string_escape")
+ return "<'%s'" % strutils.bytes_to_escaped_str(self.path)
TokValue = pp.MatchFirst(
@@ -337,7 +342,7 @@ class OptionsOrValue(_Component):
# it to be canonical. The user can specify a different case by using a
# string value literal.
self.option_used = False
- if isinstance(value, basestring):
+ if isinstance(value, six.string_types):
for i in self.options:
# Find the exact option value in a case-insensitive way
if i.lower() == value.lower():
@@ -573,4 +578,4 @@ class NestedMessage(Token):
def freeze(self, settings):
f = self.parsed.freeze(settings).spec()
- return self.__class__(TokValueLiteral(f.encode("string_escape")))
+ return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f)))
diff --git a/pathod/language/exceptions.py b/pathod/language/exceptions.py
index 84ad3c02..eb22bed2 100644
--- a/pathod/language/exceptions.py
+++ b/pathod/language/exceptions.py
@@ -1,4 +1,3 @@
-
class RenderError(Exception):
pass
diff --git a/pathod/language/generators.py b/pathod/language/generators.py
index a17e7052..9fff3082 100644
--- a/pathod/language/generators.py
+++ b/pathod/language/generators.py
@@ -2,17 +2,19 @@ import string
import random
import mmap
+import six
+
DATATYPES = dict(
- ascii_letters=string.ascii_letters,
- ascii_lowercase=string.ascii_lowercase,
- ascii_uppercase=string.ascii_uppercase,
- digits=string.digits,
- hexdigits=string.hexdigits,
- octdigits=string.octdigits,
- punctuation=string.punctuation,
- whitespace=string.whitespace,
- ascii=string.printable,
- bytes="".join(chr(i) for i in range(256))
+ ascii_letters=string.ascii_letters.encode(),
+ ascii_lowercase=string.ascii_lowercase.encode(),
+ ascii_uppercase=string.ascii_uppercase.encode(),
+ digits=string.digits.encode(),
+ hexdigits=string.hexdigits.encode(),
+ octdigits=string.octdigits.encode(),
+ punctuation=string.punctuation.encode(),
+ whitespace=string.whitespace.encode(),
+ ascii=string.printable.encode(),
+ bytes=bytes(bytearray(range(256)))
)
@@ -35,16 +37,25 @@ class TransformGenerator(object):
def __getitem__(self, x):
d = self.gen.__getitem__(x)
+ if isinstance(x, slice):
+ return self.transform(x.start, d)
return self.transform(x, d)
- def __getslice__(self, a, b):
- d = self.gen.__getslice__(a, b)
- return self.transform(a, d)
-
def __repr__(self):
return "'transform(%s)'" % self.gen
+def rand_byte(chars):
+ """
+ Return a random character as byte from a charset.
+ """
+ # bytearray has consistent behaviour on both Python 2 and 3
+ # while bytes does not
+ if six.PY2:
+ return random.choice(chars)
+ return bytes([random.choice(chars)])
+
+
class RandomGenerator(object):
def __init__(self, dtype, length):
@@ -55,12 +66,10 @@ class RandomGenerator(object):
return self.length
def __getitem__(self, x):
- return random.choice(DATATYPES[self.dtype])
-
- def __getslice__(self, a, b):
- b = min(b, self.length)
chars = DATATYPES[self.dtype]
- return "".join(random.choice(chars) for x in range(a, b))
+ if isinstance(x, slice):
+ return b"".join(rand_byte(chars) for _ in range(*x.indices(self.length)))
+ return rand_byte(chars)
def __repr__(self):
return "%s random from %s" % (self.length, self.dtype)
@@ -70,17 +79,17 @@ class FileGenerator(object):
def __init__(self, path):
self.path = path
- self.fp = file(path, "rb")
+ self.fp = open(path, "rb")
self.map = mmap.mmap(self.fp.fileno(), 0, access=mmap.ACCESS_READ)
def __len__(self):
return len(self.map)
def __getitem__(self, x):
- return self.map.__getitem__(x)
-
- def __getslice__(self, a, b):
- return self.map.__getslice__(a, b)
+ if isinstance(x, slice):
+ return self.map.__getitem__(x)
+ # A slice of length 1 returns a byte object (not an integer)
+ return self.map.__getitem__(slice(x, x + 1 or self.map.size()))
def __repr__(self):
return "<%s" % self.path
diff --git a/pathod/language/http.py b/pathod/language/http.py
index a82f12fe..b2308d5e 100644
--- a/pathod/language/http.py
+++ b/pathod/language/http.py
@@ -11,6 +11,7 @@ from . import base, exceptions, actions, message
# instead of duplicating the HTTP on-the-wire representation here.
# see http2 language for an example
+
class WS(base.CaselessLiteral):
TOK = "ws"
diff --git a/pathod/language/http2.py b/pathod/language/http2.py
index d5e3ca31..85d9047f 100644
--- a/pathod/language/http2.py
+++ b/pathod/language/http2.py
@@ -27,6 +27,7 @@ from . import base, message
h2f:42:DATA:END_STREAM,PADDED:0x1234567:'content body payload'
"""
+
def get_header(val, headers):
"""
Header keys may be Values, so we have to "generate" them as we try the
@@ -48,6 +49,7 @@ class _HeaderMixin(object):
self.value.get_generator(settings),
)
+
class _HTTP2Message(message.Message):
@property
def actions(self):
@@ -287,13 +289,10 @@ class Request(_HTTP2Message):
def spec(self):
return ":".join([i.spec() for i in self.tokens])
+
def make_error_response(reason, body=None):
tokens = [
StatusCode("800"),
Body(base.TokValueLiteral("pathod error: " + (body or reason))),
]
return Response(tokens)
-
-
-# class Frame(message.Message):
-# pass
diff --git a/pathod/language/websockets.py b/pathod/language/websockets.py
index 09443a95..9b752b7e 100644
--- a/pathod/language/websockets.py
+++ b/pathod/language/websockets.py
@@ -1,4 +1,3 @@
-import os
import random
import string
import netlib.websockets
diff --git a/pathod/language/writer.py b/pathod/language/writer.py
index 1a27e1ef..22e32ce2 100644
--- a/pathod/language/writer.py
+++ b/pathod/language/writer.py
@@ -1,6 +1,5 @@
import time
from netlib.exceptions import TcpDisconnect
-import netlib.tcp
BLOCKSIZE = 1024
# It's not clear what the upper limit for time.sleep is. It's lower than the
diff --git a/pathod/log.py b/pathod/log.py
index f203542f..5bf55de4 100644
--- a/pathod/log.py
+++ b/pathod/log.py
@@ -1,8 +1,8 @@
import datetime
-import netlib.utils
-import netlib.tcp
-import netlib.http
+import six
+
+from netlib import strutils
TIMEFMT = '%d-%m-%y %H:%M:%S'
@@ -53,17 +53,17 @@ class LogCtx(object):
]
)
if exc_value:
- raise exc_type, exc_value, traceback
+ six.reraise(exc_type, exc_value, traceback)
def suppress(self):
self.suppressed = True
def dump(self, data, hexdump):
if hexdump:
- for line in netlib.utils.hexdump(data):
+ for line in strutils.hexdump(data):
self("\t%s %s %s" % line)
else:
- for i in netlib.utils.clean_bin(data).split("\n"):
+ for i in strutils.clean_bin(data).split("\n"):
self("\t%s" % i)
def __call__(self, line):
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index a49ed351..5cfb4591 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -13,21 +13,24 @@ import threading
import OpenSSL.crypto
import six
-from netlib import tcp, http, certutils, websockets, socks
+from netlib import tcp, certutils, websockets, socks
from netlib.exceptions import HttpException, TcpDisconnect, TcpTimeout, TlsException, TcpException, \
NetlibException
from netlib.http import http1, http2
-import language.http
-import language.websockets
-from . import utils, log
+from . import log, language
import logging
from netlib.tutils import treq
+from netlib import strutils
logging.getLogger("hpack").setLevel(logging.WARNING)
+def xrepr(s):
+ return repr(s)[1:-1]
+
+
class PathocError(Exception):
pass
@@ -43,7 +46,7 @@ class SSLInfo(object):
"Cipher: %s, %s bit, %s" % self.cipher,
"SSL certificate chain:"
]
- for n,i in enumerate(self.certchain):
+ for n, i in enumerate(self.certchain):
parts.append(" Certificate [%s]" % n)
parts.append("\tSubject: ")
for cn in i.get_subject().get_components():
@@ -74,7 +77,6 @@ class SSLInfo(object):
return "\n".join(parts)
-
class WebsocketFrameReader(threading.Thread):
def __init__(
@@ -284,7 +286,7 @@ class Pathoc(tcp.TCPClient):
if self.use_http2 and not self.ssl:
raise NotImplementedError("HTTP2 without SSL is not supported.")
- tcp.TCPClient.connect(self)
+ ret = tcp.TCPClient.connect(self)
if connect_to:
self.http_connect(connect_to)
@@ -322,6 +324,7 @@ class Pathoc(tcp.TCPClient):
if self.timeout:
self.settimeout(self.timeout)
+ return ret
def stop(self):
if self.ws_framereader:
@@ -426,7 +429,7 @@ class Pathoc(tcp.TCPClient):
finally:
if resp:
lg("<< %s %s: %s bytes" % (
- resp.status_code, utils.xrepr(resp.reason), len(resp.content)
+ resp.status_code, strutils.bytes_to_escaped_str(resp.reason), len(resp.content)
))
if resp.status_code in self.ignorecodes:
lg.suppress()
diff --git a/pathod/pathod.py b/pathod/pathod.py
index 017ce072..0449c0c1 100644
--- a/pathod/pathod.py
+++ b/pathod/pathod.py
@@ -6,15 +6,11 @@ import sys
import threading
import urllib
-from netlib import tcp, http, certutils, websockets
+from netlib import tcp, certutils, websockets
from netlib.exceptions import HttpException, HttpReadDisconnect, TcpTimeout, TcpDisconnect, \
TlsException
from . import version, app, language, utils, log, protocols
-import language.http
-import language.actions
-import language.exceptions
-import language.websockets
DEFAULT_CERT_DOMAIN = "pathod.net"
@@ -116,7 +112,6 @@ class PathodHandler(tcp.BaseHandler):
return None, response_log
return self.handle_http_request, response_log
-
def handle_http_request(self, logger):
"""
Returns a (handler, log) tuple.
@@ -358,6 +353,8 @@ class Pathod(tcp.TCPServer):
staticdir=self.staticdir
)
+ self.loglock = threading.Lock()
+
def check_policy(self, req, settings):
"""
A policy check that verifies the request size is within limits.
@@ -408,8 +405,7 @@ class Pathod(tcp.TCPServer):
def add_log(self, d):
if not self.noapi:
- lock = threading.Lock()
- with lock:
+ with self.loglock:
d["id"] = self.logid
self.log.insert(0, d)
if len(self.log) > self.LOGBUF:
@@ -418,17 +414,18 @@ class Pathod(tcp.TCPServer):
return d["id"]
def clear_log(self):
- lock = threading.Lock()
- with lock:
+ with self.loglock:
self.log = []
def log_by_id(self, identifier):
- for i in self.log:
- if i["id"] == identifier:
- return i
+ with self.loglock:
+ for i in self.log:
+ if i["id"] == identifier:
+ return i
def get_log(self):
- return self.log
+ with self.loglock:
+ return self.log
def main(args): # pragma: no cover
diff --git a/pathod/pathod_cmdline.py b/pathod/pathod_cmdline.py
index 1f972a49..a4f05faf 100644
--- a/pathod/pathod_cmdline.py
+++ b/pathod/pathod_cmdline.py
@@ -4,7 +4,7 @@ import os
import os.path
import re
-from netlib import tcp
+from netlib import tcp, human
from . import pathod, version, utils
@@ -49,12 +49,12 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
help="""
URL path specifying prefix for URL crafting
commands. (%s)
- """%pathod.DEFAULT_CRAFT_ANCHOR
+ """ % pathod.DEFAULT_CRAFT_ANCHOR
)
parser.add_argument(
"--confdir",
- action="store", type = str, dest="confdir", default='~/.mitmproxy',
- help = "Configuration directory. (~/.mitmproxy)"
+ action="store", type=str, dest="confdir", default='~/.mitmproxy',
+ help="Configuration directory. (~/.mitmproxy)"
)
parser.add_argument(
"-d", dest='staticdir', default=None, type=str,
@@ -117,8 +117,8 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
)
group.add_argument(
"--cert", dest='ssl_certs', default=[], type=str,
- metavar = "SPEC", action="append",
- help = """
+ metavar="SPEC", action="append",
+ help="""
Add an SSL certificate. SPEC is of the form "[domain=]path". The domain
may include a wildcard, and is equal to "*" if not specified. The file
at path is a certificate in PEM format. If a private key is included in
@@ -177,7 +177,6 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
help="Output all received & sent HTTP/2 frames"
)
-
args = parser.parse_args(argv[1:])
args.ssl_version, args.ssl_options = tcp.sslversion_choices[args.ssl_version]
@@ -206,7 +205,7 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
sizelimit = None
if args.sizelimit:
try:
- sizelimit = utils.parse_size(args.sizelimit)
+ sizelimit = human.parse_size(args.sizelimit)
except ValueError as v:
return parser.error(v)
args.sizelimit = sizelimit
diff --git a/pathod/protocols/__init__.py b/pathod/protocols/__init__.py
index 1a8c7dab..f8f3008f 100644
--- a/pathod/protocols/__init__.py
+++ b/pathod/protocols/__init__.py
@@ -1 +1,7 @@
from . import http, http2, websockets
+
+__all__ = [
+ "http",
+ "http2",
+ "websockets",
+]
diff --git a/pathod/protocols/http.py b/pathod/protocols/http.py
index 1f1765cb..d09b5bf2 100644
--- a/pathod/protocols/http.py
+++ b/pathod/protocols/http.py
@@ -1,6 +1,6 @@
-from netlib import tcp, wsgi
-from netlib.exceptions import HttpReadDisconnect, TlsException
-from netlib.http import http1, Request
+from netlib import wsgi
+from netlib.exceptions import TlsException
+from netlib.http import http1
from .. import version, language
diff --git a/pathod/protocols/http2.py b/pathod/protocols/http2.py
index a098a14e..3f45ec80 100644
--- a/pathod/protocols/http2.py
+++ b/pathod/protocols/http2.py
@@ -1,5 +1,6 @@
from netlib.http import http2
-from .. import version, app, language, utils, log
+from .. import language
+
class HTTP2Protocol:
diff --git a/pathod/protocols/websockets.py b/pathod/protocols/websockets.py
index 134d27bc..2b60e618 100644
--- a/pathod/protocols/websockets.py
+++ b/pathod/protocols/websockets.py
@@ -18,7 +18,7 @@ class WebsocketsProtocol:
frm = websockets.Frame.from_file(self.pathod_handler.rfile)
except NetlibException as e:
lg("Error reading websocket frame: %s" % e)
- break
+ return None, None
ended = time.time()
lg(frm.human_readable())
retlog = dict(
diff --git a/pathod/test.py b/pathod/test.py
index 23b7a5b6..11462729 100644
--- a/pathod/test.py
+++ b/pathod/test.py
@@ -1,12 +1,14 @@
from six.moves import cStringIO as StringIO
import threading
+import time
+
from six.moves import queue
-import requests
-import requests.packages.urllib3
from . import pathod
-requests.packages.urllib3.disable_warnings()
+
+class TimeoutError(Exception):
+ pass
class Daemon:
@@ -39,39 +41,51 @@ class Daemon:
"""
return "%s/p/%s" % (self.urlbase, spec)
- def info(self):
- """
- Return some basic info about the remote daemon.
- """
- resp = requests.get("%s/api/info" % self.urlbase, verify=False)
- return resp.json()
-
def text_log(self):
return self.logfp.getvalue()
+ def wait_for_silence(self, timeout=5):
+ start = time.time()
+ while 1:
+ if time.time() - start >= timeout:
+ raise TimeoutError(
+ "%s service threads still alive" %
+ self.thread.server.handler_counter.count
+ )
+ if self.thread.server.handler_counter.count == 0:
+ return
+
+ def expect_log(self, n, timeout=5):
+ l = []
+ start = time.time()
+ while True:
+ l = self.log()
+ if time.time() - start >= timeout:
+ return None
+ if len(l) >= n:
+ break
+ return l
+
def last_log(self):
"""
Returns the last logged request, or None.
"""
- l = self.log()
+ l = self.expect_log(1)
if not l:
return None
- return l[0]
+ return l[-1]
def log(self):
"""
Return the log buffer as a list of dictionaries.
"""
- resp = requests.get("%s/api/log" % self.urlbase, verify=False)
- return resp.json()["log"]
+ return self.thread.server.get_log()
def clear_log(self):
"""
Clear the log.
"""
- self.logfp.truncate(0)
- resp = requests.get("%s/api/clear_log" % self.urlbase, verify=False)
- return resp.ok
+ return self.thread.server.clear_log()
def shutdown(self):
"""
@@ -88,6 +102,7 @@ class _PaThread(threading.Thread):
self.name = "PathodThread"
self.iface, self.q, self.ssl = iface, q, ssl
self.daemonargs = daemonargs
+ self.server = None
def run(self):
self.server = pathod.Pathod(
diff --git a/pathod/utils.py b/pathod/utils.py
index d1e2dd00..3276198a 100644
--- a/pathod/utils.py
+++ b/pathod/utils.py
@@ -3,15 +3,6 @@ import sys
import netlib.utils
-SIZE_UNITS = dict(
- b=1024 ** 0,
- k=1024 ** 1,
- m=1024 ** 2,
- g=1024 ** 3,
- t=1024 ** 4,
-)
-
-
class MemBool(object):
"""
@@ -26,20 +17,6 @@ class MemBool(object):
return bool(v)
-def parse_size(s):
- try:
- return int(s)
- except ValueError:
- pass
- for i in SIZE_UNITS.keys():
- if s.endswith(i):
- try:
- return int(s[:-1]) * SIZE_UNITS[i]
- except ValueError:
- break
- raise ValueError("Invalid size specification.")
-
-
def parse_anchor_spec(s):
"""
Return a tuple, or None on error.
@@ -49,33 +26,6 @@ def parse_anchor_spec(s):
return tuple(s.split("=", 1))
-def xrepr(s):
- return repr(s)[1:-1]
-
-
-def inner_repr(s):
- """
- Returns the inner portion of a string or unicode repr (i.e. without the
- quotes)
- """
- if isinstance(s, unicode):
- return repr(s)[2:-1]
- else:
- return repr(s)[1:-1]
-
-
-def escape_unprintables(s):
- """
- Like inner_repr, but preserves line breaks.
- """
- s = s.replace("\r\n", "PATHOD_MARKER_RN")
- s = s.replace("\n", "PATHOD_MARKER_N")
- s = inner_repr(s)
- s = s.replace("PATHOD_MARKER_RN", "\n")
- s = s.replace("PATHOD_MARKER_N", "\n")
- return s
-
-
data = netlib.utils.Data(__name__)
diff --git a/pathod/version.py b/pathod/version.py
index 2da7637d..3441be92 100644
--- a/pathod/version.py
+++ b/pathod/version.py
@@ -4,3 +4,10 @@ from netlib.version import VERSION, IVERSION
NAME = "pathod"
NAMEVERSION = NAME + " " + VERSION
+
+__all__ = [
+ "NAME",
+ "NAMEVERSION",
+ "VERSION",
+ "IVERSION",
+]