mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2025-12-16 01:30:07 +01:00
Phoenix:
- Reformat the documentation generator to take into account the "adv" sub-package; - Make the `sphinx_generator.py` and the `sphinxtools` package Python 3 - compliant. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71589 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -69,9 +69,12 @@ REMOVED_LINKS = ['Library:', 'Category:', 'Predefined objects/pointers:']
|
||||
# wx.html and so on)
|
||||
MODULENAME_REPLACE = {'_core' : '',
|
||||
'_dataview': 'dataview.',
|
||||
'_adv' : '',
|
||||
'_adv' : 'adv.',
|
||||
}
|
||||
|
||||
NO_MODULE = {'DatePickerCtrlGeneric': 'adv.',
|
||||
'GenericCalendarCtrl': 'adv.'}
|
||||
|
||||
# Other C++ specific things to strip away
|
||||
CPP_ITEMS = ['*', '&', 'const', 'unsigned', '(size_t)', 'size_t', 'void']
|
||||
|
||||
@@ -88,11 +91,12 @@ SPHINXROOT = os.path.join(PHOENIXROOT, 'docs', 'sphinx')
|
||||
# converted Python snippets in 3 sub-folders
|
||||
SNIPPETROOT = os.path.join(SPHINXROOT, 'rest_substitutions', 'snippets')
|
||||
|
||||
# A folder where some of the difficult-to-translate-to-ReST tables are. There are 3 of
|
||||
# A folder where some of the difficult-to-translate-to-ReST tables are. There are 4 of
|
||||
# them up to now, for various reasons:
|
||||
# 1. The wx.Sizer flags table is a grid table, very difficult to ReSTify automatically
|
||||
# 2. The wx.ColourDatabase table of colour comes up all messy when ReSTified from XML
|
||||
# 3. The "wxWidgets 2.8 Compatibility Functions" table for wx.VScrolledWindow
|
||||
# 4. The wx.ArtProvider table
|
||||
TABLEROOT = os.path.join(SPHINXROOT, 'rest_substitutions', 'tables')
|
||||
|
||||
# Folder where to save the inheritance diagrams for the classes
|
||||
|
||||
@@ -13,17 +13,23 @@
|
||||
# Standard library imports
|
||||
|
||||
import os
|
||||
import sys
|
||||
import errno
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
# Phoenix-specific imports
|
||||
|
||||
from utilities import Wx2Sphinx, FormatExternalLink
|
||||
from constants import INHERITANCEROOT
|
||||
from .utilities import Wx2Sphinx, FormatExternalLink
|
||||
from .constants import INHERITANCEROOT
|
||||
|
||||
ENOENT = getattr(errno, 'ENOENT', 0)
|
||||
EPIPE = getattr(errno, 'EPIPE', 0)
|
||||
|
||||
if sys.version_info < (3, ):
|
||||
string_base = basestring
|
||||
else:
|
||||
string_base = str
|
||||
|
||||
|
||||
class InheritanceDiagram(object):
|
||||
"""
|
||||
@@ -36,7 +42,7 @@ class InheritanceDiagram(object):
|
||||
|
||||
if main_class is None:
|
||||
self.class_info, self.specials = classes
|
||||
self.class_info = self.class_info.values()
|
||||
self.class_info = list(self.class_info.values())
|
||||
else:
|
||||
self.class_info, self.specials = self._class_info(classes)
|
||||
|
||||
@@ -69,7 +75,7 @@ class InheritanceDiagram(object):
|
||||
recurse(cls)
|
||||
specials.append(self.class_name(cls)[1])
|
||||
|
||||
return all_classes.values(), specials
|
||||
return list(all_classes.values()), specials
|
||||
|
||||
|
||||
def class_name(self, cls):
|
||||
@@ -117,10 +123,10 @@ class InheritanceDiagram(object):
|
||||
}
|
||||
|
||||
def _format_node_attrs(self, attrs):
|
||||
return ','.join(['%s=%s' % x for x in attrs.items()])
|
||||
return ','.join(['%s=%s' % x for x in list(attrs.items())])
|
||||
|
||||
def _format_graph_attrs(self, attrs):
|
||||
return ''.join(['%s=%s;\n' % x for x in attrs.items()])
|
||||
return ''.join(['%s=%s;\n' % x for x in list(attrs.items())])
|
||||
|
||||
def generate_dot(self, class_summary, name="dummy", graph_attrs={}, node_attrs={}, edge_attrs={}):
|
||||
"""Generate a graphviz dot graph from the classes that were passed in
|
||||
@@ -240,7 +246,7 @@ class InheritanceDiagram(object):
|
||||
code = self.generate_dot(class_summary)
|
||||
|
||||
# graphviz expects UTF-8 by default
|
||||
if isinstance(code, unicode):
|
||||
if isinstance(code, string_base):
|
||||
code = code.encode('utf-8')
|
||||
|
||||
dot_args = ['dot']
|
||||
@@ -257,19 +263,19 @@ class InheritanceDiagram(object):
|
||||
|
||||
p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
|
||||
|
||||
except OSError, err:
|
||||
except OSError as err:
|
||||
|
||||
if err.errno != ENOENT: # No such file or directory
|
||||
raise
|
||||
|
||||
print '\nERROR: Graphviz command `dot` cannot be run (needed for Graphviz output), check your ``PATH`` setting'
|
||||
print('\nERROR: Graphviz command `dot` cannot be run (needed for Graphviz output), check your ``PATH`` setting')
|
||||
|
||||
try:
|
||||
# Graphviz may close standard input when an error occurs,
|
||||
# resulting in a broken pipe on communicate()
|
||||
stdout, stderr = p.communicate(code)
|
||||
|
||||
except OSError, err:
|
||||
except OSError as err:
|
||||
|
||||
# in this case, read the standard output and standard error streams
|
||||
# directly, to get the error message(s)
|
||||
@@ -277,7 +283,7 @@ class InheritanceDiagram(object):
|
||||
p.wait()
|
||||
|
||||
if p.returncode != 0:
|
||||
print '\nERROR: Graphviz `dot` command exited with error:\n[stderr]\n%s\n[stdout]\n%s\n\n' % (stderr, stdout)
|
||||
print(('\nERROR: Graphviz `dot` command exited with error:\n[stderr]\n%s\n[stdout]\n%s\n\n' % (stderr, stdout)))
|
||||
|
||||
fid = open(mapfile, 'rt')
|
||||
map = fid.read()
|
||||
|
||||
@@ -2,21 +2,25 @@ import sys
|
||||
import os
|
||||
import operator
|
||||
import re
|
||||
import cPickle
|
||||
|
||||
from StringIO import StringIO
|
||||
if sys.version_info < (3,):
|
||||
import cPickle as pickle
|
||||
from StringIO import StringIO
|
||||
else:
|
||||
import pickle
|
||||
from io import StringIO
|
||||
|
||||
from inspect import getmro, getclasstree, getdoc, getcomments
|
||||
|
||||
from utilities import MakeSummary, ChopDescription, WriteSphinxOutput
|
||||
from utilities import FindControlImages, FormatExternalLink
|
||||
from constants import object_types, MODULE_TO_ICON, DOXY_2_REST, SPHINXROOT
|
||||
import templates
|
||||
from .utilities import MakeSummary, ChopDescription, WriteSphinxOutput
|
||||
from .utilities import FindControlImages, FormatExternalLink, IsPython3
|
||||
from .constants import object_types, MODULE_TO_ICON, DOXY_2_REST, SPHINXROOT
|
||||
from . import templates
|
||||
|
||||
EPYDOC_PATTERN = re.compile(r'\S+{\S+}', re.DOTALL)
|
||||
|
||||
reload(sys)
|
||||
sys.setdefaultencoding("utf-8")
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
|
||||
def make_class_tree(tree):
|
||||
@@ -271,8 +275,8 @@ class ParentBase(object):
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
|
||||
self.docs = u''
|
||||
self.comments = u''
|
||||
self.docs = ''
|
||||
self.comments = ''
|
||||
|
||||
self.is_redundant = False
|
||||
|
||||
@@ -281,7 +285,7 @@ class ParentBase(object):
|
||||
|
||||
def Add(self, klass):
|
||||
|
||||
if u'lambda' in klass.name:
|
||||
if 'lambda' in klass.name:
|
||||
return
|
||||
|
||||
for child in self.children:
|
||||
@@ -297,13 +301,13 @@ class ParentBase(object):
|
||||
if self.GetShortName().startswith('__test') or '.extern.' in self.name:
|
||||
self.is_redundant = True
|
||||
|
||||
self.children = sorted(self.children, key=lambda k: (getattr(k, "order"), getattr(k, "name").lower()))
|
||||
self.children = sorted(self.children, key=lambda k: (getattr(k, 'order'), getattr(k, 'name').lower()))
|
||||
|
||||
if self.docs is None:
|
||||
self.docs = u''
|
||||
self.docs = ''
|
||||
|
||||
if self.comments is None or not self.comments.strip():
|
||||
self.comments = u''
|
||||
self.comments = ''
|
||||
|
||||
for child in self.children:
|
||||
child.Save()
|
||||
@@ -321,7 +325,7 @@ class ParentBase(object):
|
||||
|
||||
def GetShortName(self):
|
||||
|
||||
return self.name.split(".")[-1]
|
||||
return self.name.split('.')[-1]
|
||||
|
||||
|
||||
def GetObject(self):
|
||||
@@ -349,7 +353,7 @@ class ParentBase(object):
|
||||
|
||||
total = count
|
||||
|
||||
for n in xrange(count):
|
||||
for n in range(count):
|
||||
total += self.children[n].GetChildrenCount()
|
||||
|
||||
return total
|
||||
@@ -396,10 +400,10 @@ class Library(ParentBase):
|
||||
ParentBase.__init__(self, name, object_types.LIBRARY)
|
||||
|
||||
self.parent = None
|
||||
self.filename = u''
|
||||
self.filename = ''
|
||||
self.order = 0
|
||||
self.obj_type = u"Library"
|
||||
self.python_version = u''
|
||||
self.obj_type = 'Library'
|
||||
self.python_version = ''
|
||||
|
||||
self.sphinx_file = MakeSphinxFile(name)
|
||||
self.base_name = name
|
||||
@@ -478,7 +482,7 @@ class Library(ParentBase):
|
||||
|
||||
def ToRest(self, class_summary):
|
||||
|
||||
print '\n\nReST-ifying %s...\n\n'%self.base_name
|
||||
print(('\n\nReST-ifying %s...\n\n'%self.base_name))
|
||||
stream = StringIO()
|
||||
|
||||
header = templates.TEMPLATE_DESCRIPTION%(self.base_name, self.base_name)
|
||||
@@ -526,14 +530,14 @@ class Library(ParentBase):
|
||||
|
||||
if os.path.isfile(pickle_file):
|
||||
fid = open(pickle_file, 'rb')
|
||||
items = cPickle.load(fid)
|
||||
items = pickle.load(fid)
|
||||
fid.close()
|
||||
else:
|
||||
items = {}
|
||||
|
||||
items.update(class_dict)
|
||||
fid = open(pickle_file, 'wb')
|
||||
cPickle.dump(items, fid)
|
||||
pickle.dump(items, fid)
|
||||
fid.close()
|
||||
|
||||
|
||||
@@ -543,11 +547,11 @@ class Module(ParentBase):
|
||||
|
||||
ParentBase.__init__(self, name, kind)
|
||||
|
||||
self.filename = u''
|
||||
self.filename = ''
|
||||
self.sphinx_file = MakeSphinxFile(name)
|
||||
|
||||
if kind == object_types.PACKAGE:
|
||||
self.obj_type = u"Package"
|
||||
self.obj_type = 'Package'
|
||||
self.order = kind
|
||||
return
|
||||
|
||||
@@ -588,14 +592,14 @@ class Module(ParentBase):
|
||||
spacer = ' '*self.name.count('.')
|
||||
|
||||
if self.kind != object_types.PACKAGE:
|
||||
print '%s - %s (module)'%(spacer, self.name)
|
||||
print(('%s - %s (module)'%(spacer, self.name)))
|
||||
if self.inheritance_diagram:
|
||||
png, map = self.inheritance_diagram.MakeInheritanceDiagram(class_summary)
|
||||
short_name = self.GetShortName()
|
||||
image_desc = templates.TEMPLATE_INHERITANCE % ('module', short_name, png, short_name, map)
|
||||
stream.write(image_desc)
|
||||
else:
|
||||
print '%s - %s (package)'%(spacer, self.name)
|
||||
print(('%s - %s (package)'%(spacer, self.name)))
|
||||
|
||||
generic_summary(self, stream)
|
||||
|
||||
@@ -645,10 +649,10 @@ class Class(ParentBase):
|
||||
for item in sups:
|
||||
item = repr(item)
|
||||
|
||||
sup = item.replace("<class ", "").replace(">", "").replace("<type ", "")
|
||||
sup = sup.strip().replace('"', "").replace("'", "")
|
||||
if " at " in sup:
|
||||
sup = sup[0:sup.index(" at ")].strip()
|
||||
sup = item.replace('<class ', '').replace('>', '').replace('<type ', '')
|
||||
sup = sup.strip().replace('"', '').replace("'", '')
|
||||
if ' at ' in sup:
|
||||
sup = sup[0:sup.index(' at ')].strip()
|
||||
|
||||
if 'wx._' in sup:
|
||||
name_parts = sup.split('.')
|
||||
@@ -687,11 +691,11 @@ class Class(ParentBase):
|
||||
self.subClasses = sortedSubClasses
|
||||
self.superClasses = sortedSupClasses
|
||||
|
||||
self.signature = ""
|
||||
self.signature = ''
|
||||
self.inheritance_diagram = None
|
||||
|
||||
self.order = 3
|
||||
self.obj_type = u"Class"
|
||||
self.obj_type = 'Class'
|
||||
self.sphinx_file = MakeSphinxFile(name)
|
||||
|
||||
|
||||
@@ -741,7 +745,7 @@ class Class(ParentBase):
|
||||
generic_summary(self, stream)
|
||||
|
||||
stream.write(templates.TEMPLATE_API)
|
||||
stream.write("\n.. class:: %s\n\n"%self.signature)
|
||||
stream.write('\n.. class:: %s\n\n'%self.signature)
|
||||
|
||||
docs = ''
|
||||
for line in class_docs.splitlines(True):
|
||||
@@ -815,8 +819,8 @@ class ChildrenBase(object):
|
||||
|
||||
self.order = 4
|
||||
|
||||
self.docs = u''
|
||||
self.comments = u''
|
||||
self.docs = ''
|
||||
self.comments = ''
|
||||
|
||||
self.is_redundant = False
|
||||
|
||||
@@ -835,7 +839,7 @@ class ChildrenBase(object):
|
||||
|
||||
def GetShortName(self):
|
||||
|
||||
return self.name.split(".")[-1]
|
||||
return self.name.split('.')[-1]
|
||||
|
||||
|
||||
def GetChildren(self):
|
||||
@@ -856,10 +860,10 @@ class ChildrenBase(object):
|
||||
def Save(self):
|
||||
|
||||
if self.docs is None:
|
||||
self.docs = u''
|
||||
self.docs = ''
|
||||
|
||||
if self.comments is None or not self.comments.strip():
|
||||
self.comments = u''
|
||||
self.comments = ''
|
||||
|
||||
|
||||
def ToRest(self, class_summary):
|
||||
@@ -876,9 +880,9 @@ class Method(ChildrenBase):
|
||||
self.order = 5
|
||||
|
||||
self.arguments = []
|
||||
self.signature = u''
|
||||
self.signature = ''
|
||||
|
||||
self.obj_type = u"Method/Function"
|
||||
self.obj_type = 'Method/Function'
|
||||
|
||||
|
||||
def Save(self):
|
||||
@@ -888,8 +892,8 @@ class Method(ChildrenBase):
|
||||
newargs = []
|
||||
if self.arguments and any(self.arguments[0]):
|
||||
for name, repr_val, eval_val in self.arguments:
|
||||
repr_val = (repr_val is not None and [repr_val] or [""])[0]
|
||||
eval_val = (eval_val is not None and [eval_val] or [""])[0]
|
||||
repr_val = (repr_val is not None and [repr_val] or [''])[0]
|
||||
eval_val = (eval_val is not None and [eval_val] or [''])[0]
|
||||
newargs.append((name, repr_val, eval_val))
|
||||
|
||||
self.arguments = newargs
|
||||
@@ -954,7 +958,7 @@ class Property(ChildrenBase):
|
||||
|
||||
ChildrenBase.__init__(self, name, object_types.PROPERTY)
|
||||
|
||||
self.getter = self.setter = self.deleter = ""
|
||||
self.getter = self.setter = self.deleter = ''
|
||||
|
||||
try:
|
||||
if item.fget:
|
||||
@@ -975,7 +979,7 @@ class Property(ChildrenBase):
|
||||
self.docs = getdoc(item)
|
||||
self.comments = getcomments(item)
|
||||
|
||||
self.obj_type = u"Property"
|
||||
self.obj_type = 'Property'
|
||||
self.order = 6
|
||||
|
||||
|
||||
@@ -1003,7 +1007,11 @@ class Attribute(ChildrenBase):
|
||||
|
||||
def __init__(self, name, specs, value):
|
||||
|
||||
specs = unicode(specs)
|
||||
if IsPython3():
|
||||
specs = str(specs)
|
||||
else:
|
||||
specs = unicode(specs)
|
||||
|
||||
start, end = specs.find("'"), specs.rfind("'")
|
||||
specs = specs[start+1:end]
|
||||
|
||||
@@ -1014,7 +1022,7 @@ class Attribute(ChildrenBase):
|
||||
kind = getattr(object_types, uspecs)
|
||||
except AttributeError:
|
||||
try:
|
||||
uspecs = uspecs + u"TYPE"
|
||||
uspecs = uspecs + 'TYPE'
|
||||
kind = getattr(object_types, uspecs)
|
||||
except AttributeError:
|
||||
kind = object_types.UNKNOWNTYPE
|
||||
@@ -1022,9 +1030,9 @@ class Attribute(ChildrenBase):
|
||||
try:
|
||||
reprValue = repr(value.__class__)
|
||||
except (NameError, AttributeError):
|
||||
reprValue = ""
|
||||
reprValue = ''
|
||||
|
||||
if u"class" in strValue or u"class" in reprValue:
|
||||
if 'class' in strValue or 'class' in reprValue:
|
||||
kind = object_types.INSTANCETYPE
|
||||
|
||||
ChildrenBase.__init__(self, name, kind)
|
||||
@@ -1035,9 +1043,9 @@ class Attribute(ChildrenBase):
|
||||
try:
|
||||
self.docs = getdoc(value)
|
||||
except (NameError, AttributeError):
|
||||
self.docs = u''
|
||||
self.docs = ''
|
||||
|
||||
self.obj_type = u"Attribute"
|
||||
self.obj_type = 'Attribute'
|
||||
self.order = 7
|
||||
|
||||
|
||||
|
||||
@@ -8,29 +8,34 @@ import os
|
||||
import sys
|
||||
import glob
|
||||
import types
|
||||
import cPickle
|
||||
import imp
|
||||
import traceback
|
||||
import pkgutil
|
||||
|
||||
import __builtin__
|
||||
|
||||
if sys.version_info < (3,):
|
||||
import cPickle as pickle
|
||||
else:
|
||||
import pickle
|
||||
|
||||
from buildtools.config import phoenixDir
|
||||
|
||||
from inspect import getargspec, ismodule, getdoc, getmodule, getcomments, isfunction
|
||||
from inspect import ismethoddescriptor, getsource, ismemberdescriptor, isgetsetdescriptor
|
||||
from inspect import isbuiltin, isclass, getfile, ismethod
|
||||
|
||||
from librarydescription import Library, Module, Class
|
||||
from librarydescription import Method, Property, Attribute
|
||||
from .librarydescription import Library, Module, Class
|
||||
from .librarydescription import Method, Property, Attribute
|
||||
|
||||
import inheritance
|
||||
from . import inheritance
|
||||
|
||||
from constants import object_types, EXCLUDED_ATTRS, MODULE_TO_ICON
|
||||
from constants import CONSTANT_RE
|
||||
from .utilities import IsPython3
|
||||
from .constants import object_types, EXCLUDED_ATTRS, MODULE_TO_ICON
|
||||
from .constants import CONSTANT_RE
|
||||
|
||||
reload(sys)
|
||||
sys.setdefaultencoding("utf-8")
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
try:
|
||||
import wx
|
||||
@@ -53,17 +58,17 @@ except ImportError:
|
||||
|
||||
import wx
|
||||
|
||||
print '\nUSING VERSION: %s\n'%wx.VERSION_STRING
|
||||
print(('\nUSING VERSION: %s\n'%wx.VERSION_STRING))
|
||||
|
||||
|
||||
if hasattr(os.path, "relpath"):
|
||||
if hasattr(os.path, 'relpath'):
|
||||
relpath = os.path.relpath # since Python 2.6
|
||||
else:
|
||||
def relpath(path, start=os.path.curdir):
|
||||
"""Return a relative version of a path"""
|
||||
|
||||
if not path:
|
||||
raise ValueError("no path specified")
|
||||
raise ValueError('no path specified')
|
||||
|
||||
start_list = os.path.abspath(start).split(os.path.sep)
|
||||
path_list = os.path.abspath(path).split(os.path.sep)
|
||||
@@ -81,7 +86,7 @@ else:
|
||||
def format_traceback():
|
||||
|
||||
t, v = sys.exc_info()[:2]
|
||||
message = "".join(traceback.format_exception_only(t, v)).replace("\n", " ")
|
||||
message = ''.join(traceback.format_exception_only(t, v)).replace('\n', ' ')
|
||||
return message.strip()
|
||||
|
||||
|
||||
@@ -89,18 +94,18 @@ def format_method(method):
|
||||
|
||||
method = method.strip()
|
||||
|
||||
if "def " not in method:
|
||||
if 'def ' not in method:
|
||||
return None, None
|
||||
|
||||
indx1, indx2, indx3 = method.index("def "), method.index("("), method.rindex(")")
|
||||
indx1, indx2, indx3 = method.index('def '), method.index('('), method.rindex(')')
|
||||
name = method[indx1+4:indx2]
|
||||
signature = method[indx2+1:indx3]
|
||||
|
||||
if "\n" in signature:
|
||||
sig = signature.split("\n")
|
||||
params = ""
|
||||
if '\n' in signature:
|
||||
sig = signature.split('\n')
|
||||
params = ''
|
||||
for s in sig:
|
||||
params += s.strip() + " "
|
||||
params += s.strip() + ' '
|
||||
params = params.rstrip()
|
||||
else:
|
||||
params = signature
|
||||
@@ -110,10 +115,10 @@ def format_method(method):
|
||||
|
||||
def analyze_params(obj, signature):
|
||||
|
||||
params = signature.split(",")
|
||||
if "self" in params:
|
||||
params.remove("self")
|
||||
signature = ",".join(params)
|
||||
params = signature.split(',')
|
||||
if 'self' in params:
|
||||
params.remove('self')
|
||||
signature = ','.join(params)
|
||||
|
||||
param_tuple = []
|
||||
|
||||
@@ -145,17 +150,20 @@ def analyze_params(obj, signature):
|
||||
p = par.strip()
|
||||
pvalue = peval = None
|
||||
|
||||
if "=" in p:
|
||||
all_values = p.split("=")
|
||||
pname, pvalue = all_values[0].strip(), "=".join(all_values[1:]).strip()
|
||||
if '=' in p:
|
||||
all_values = p.split('=')
|
||||
pname, pvalue = all_values[0].strip(), '='.join(all_values[1:]).strip()
|
||||
pvalue = pvalue.strip()
|
||||
if pname in pevals:
|
||||
try:
|
||||
peval = unicode(pevals[pname])
|
||||
if IsPython3():
|
||||
peval = str(pevals[pname])
|
||||
else:
|
||||
peval = unicode(pevals[pname])
|
||||
except UnicodeDecodeError:
|
||||
peval = repr(pevals[pname])
|
||||
except TypeError:
|
||||
peval = u''
|
||||
peval = ''
|
||||
else:
|
||||
pname = p
|
||||
|
||||
@@ -166,17 +174,17 @@ def analyze_params(obj, signature):
|
||||
|
||||
def get_constructor(source):
|
||||
|
||||
description = ""
|
||||
description = ''
|
||||
hasComma = False
|
||||
|
||||
for line in source.split("\n"):
|
||||
for line in source.split('\n'):
|
||||
|
||||
if '#' in line:
|
||||
line = line[0:line.index('#')].strip()
|
||||
|
||||
if ":" in line:
|
||||
if ':' in line:
|
||||
hasComma = True
|
||||
commaPos = line.index(":")
|
||||
commaPos = line.index(':')
|
||||
|
||||
if ('"""' in line or "'''" in line) and hasComma:
|
||||
break
|
||||
@@ -186,9 +194,9 @@ def get_constructor(source):
|
||||
if defPos > commaPos:
|
||||
break
|
||||
|
||||
description += " " + line.strip()
|
||||
description += ' ' + line.strip()
|
||||
|
||||
if "):" in line or ") :" in line or ") :" in line:
|
||||
if '):' in line or ') :' in line or ') :' in line:
|
||||
break
|
||||
|
||||
return description
|
||||
@@ -207,17 +215,19 @@ def inspect_source(method_class, obj, source):
|
||||
method_class.arguments = param_tuple
|
||||
method_class.signature = description.strip()
|
||||
|
||||
if "classmethod " in description or is_classmethod(obj):
|
||||
if 'classmethod ' in description or is_classmethod(obj):
|
||||
method_class.kind = object_types.CLASS_METHOD
|
||||
elif "staticmethod " in description:
|
||||
elif 'staticmethod ' in description:
|
||||
method_class.kind = object_types.STATIC_METHOD
|
||||
|
||||
|
||||
def is_classmethod(instancemethod):
|
||||
" Determine if an instancemethod is a classmethod. "
|
||||
""" Determine if an instancemethod is a classmethod. """
|
||||
|
||||
if hasattr(instancemethod, 'im_self'):
|
||||
return instancemethod.im_self is not None
|
||||
attribute = (IsPython3() and ['__self__'] or ['im_self'] )[0]
|
||||
|
||||
if hasattr(instancemethod, attribute):
|
||||
return getattr(instancemethod, attribute) is not None
|
||||
|
||||
return False
|
||||
|
||||
@@ -235,10 +245,10 @@ def describe_func(obj, parent_class, module_name):
|
||||
# Funny comtypes...
|
||||
return
|
||||
|
||||
if name.startswith("_") and "__init__" not in name:
|
||||
if name.startswith('_') and '__init__' not in name:
|
||||
return
|
||||
|
||||
name = parent_class.name + "." + name
|
||||
name = parent_class.name + '.' + name
|
||||
|
||||
docs = getdoc(obj)
|
||||
comments = getcomments(obj)
|
||||
@@ -256,7 +266,7 @@ def describe_func(obj, parent_class, module_name):
|
||||
try:
|
||||
source_code = getsource(obj)
|
||||
except (IOError, TypeError):
|
||||
source_code = ""
|
||||
source_code = ''
|
||||
|
||||
klass = Method(name, method)
|
||||
klass.docs = docs
|
||||
@@ -267,23 +277,32 @@ def describe_func(obj, parent_class, module_name):
|
||||
|
||||
if source_code:
|
||||
inspect_source(klass, obj, source_code)
|
||||
klass.number_lines = "%d"%len(source_code.split("\n"))
|
||||
klass.number_lines = '%d'%len(source_code.split('\n'))
|
||||
|
||||
if isinstance(obj, staticmethod):
|
||||
klass.method = method = object_types.STATIC_METHOD
|
||||
|
||||
try:
|
||||
if method in [object_types.METHOD, object_types.METHOD_DESCRIPTOR, object_types.INSTANCE_METHOD]:
|
||||
code = obj.im_func.func_code
|
||||
if IsPython3():
|
||||
code = obj.__func__.__code__
|
||||
else:
|
||||
code = obj.im_func.func_code
|
||||
elif method == object_types.STATIC_METHOD:
|
||||
code = obj.im_func.func_code
|
||||
if IsPython3():
|
||||
code = obj.__func__.__code__
|
||||
else:
|
||||
code = obj.im_func.func_code
|
||||
else:
|
||||
code = obj.func_code
|
||||
if IsPython3():
|
||||
obj.__code__
|
||||
else:
|
||||
code = obj.func_code
|
||||
except AttributeError:
|
||||
code = None
|
||||
|
||||
if code is not None:
|
||||
klass.firstlineno = "%d"%code.co_firstlineno
|
||||
klass.firstlineno = '%d'%code.co_firstlineno
|
||||
|
||||
parent_class.Add(klass)
|
||||
|
||||
@@ -296,10 +315,10 @@ def describe_class(obj, module_class, module_name, constants):
|
||||
|
||||
class_name = obj.__name__
|
||||
|
||||
if class_name == "object":
|
||||
if class_name == 'object':
|
||||
return
|
||||
|
||||
class_name = module_class.name + "." + class_name
|
||||
class_name = module_class.name + '.' + class_name
|
||||
|
||||
docs = getdoc(obj)
|
||||
comments = getcomments(obj)
|
||||
@@ -312,7 +331,7 @@ def describe_class(obj, module_class, module_name, constants):
|
||||
|
||||
for name in obj_dict:
|
||||
|
||||
if name.startswith("_") and "__init__" not in name:
|
||||
if name.startswith('_') and '__init__' not in name:
|
||||
continue
|
||||
|
||||
if name in EXCLUDED_ATTRS:
|
||||
@@ -340,7 +359,7 @@ def describe_class(obj, module_class, module_name, constants):
|
||||
count += 1
|
||||
describe_class(item, klass, module_name, constants)
|
||||
else:
|
||||
name = class_name + "." + name
|
||||
name = class_name + '.' + name
|
||||
if isinstance(item, property):
|
||||
item_class = Property(name, item)
|
||||
klass.Add(item_class)
|
||||
@@ -372,7 +391,7 @@ def describe_class(obj, module_class, module_name, constants):
|
||||
try:
|
||||
source_code = getsource(obj)
|
||||
except (IOError, TypeError):
|
||||
source_code = ""
|
||||
source_code = ''
|
||||
|
||||
if source_code:
|
||||
description = get_constructor(source_code)
|
||||
@@ -380,7 +399,7 @@ def describe_class(obj, module_class, module_name, constants):
|
||||
description = description[0:description.index(':')]
|
||||
|
||||
klass.signature = description.strip()
|
||||
klass.number_lines = '%d'%len(source_code.split("\n"))
|
||||
klass.number_lines = '%d'%len(source_code.split('\n'))
|
||||
|
||||
|
||||
def describe_module(module, kind, constants=[]):
|
||||
@@ -431,7 +450,7 @@ def describe_module(module, kind, constants=[]):
|
||||
count +=1
|
||||
describe_func(obj, klass, module_name)
|
||||
else:
|
||||
attribute = Attribute(module_name + "." + name, type(obj), obj)
|
||||
attribute = Attribute(module_name + '.' + name, type(obj), obj)
|
||||
klass.Add(attribute)
|
||||
|
||||
if constants:
|
||||
@@ -463,8 +482,7 @@ def Import(init_name, import_name, full_process=True):
|
||||
except (ImportError, NameError):
|
||||
|
||||
message = format_traceback()
|
||||
message += ' Please check that the "Import command" text box is correctly filled'
|
||||
print "Error: %s"%message
|
||||
print(('Error: %s'%message))
|
||||
|
||||
if not full_process:
|
||||
sys.path = path[:]
|
||||
@@ -482,10 +500,10 @@ def Import(init_name, import_name, full_process=True):
|
||||
try:
|
||||
version = mainmod.__VERSION__
|
||||
except AttributeError:
|
||||
print "Warning: Library '%s' has no __version__ or __VERSION__ attribute. Please specify it in the 'Import command' textbox"%import_name
|
||||
print(('Warning: Library "%s" has no __version__ or __VERSION__ attribute.'%import_name))
|
||||
return
|
||||
|
||||
print version
|
||||
print(version)
|
||||
|
||||
return mainmod
|
||||
|
||||
@@ -494,9 +512,9 @@ def PrintProgress(name, looped_names):
|
||||
|
||||
looped_names.append(name)
|
||||
if len(looped_names) == 4:
|
||||
message = ", ".join(looped_names)
|
||||
message = ', '.join(looped_names)
|
||||
looped_names = []
|
||||
print message
|
||||
print(message)
|
||||
|
||||
return looped_names
|
||||
|
||||
@@ -517,7 +535,7 @@ def SubImport(import_string, module, parent_class, ispkg):
|
||||
except:
|
||||
# pubsub and Editra can be funny sometimes...
|
||||
message = "Unable to import module/package '%s.%s'.\n Exception was: %s"%(import_string, module, format_traceback())
|
||||
print "\nWARNING: %s\n"%message
|
||||
print(('\nWARNING: %s\n'%message))
|
||||
return None, 0
|
||||
|
||||
if not ismodule(submod):
|
||||
@@ -538,12 +556,12 @@ def SubImport(import_string, module, parent_class, ispkg):
|
||||
|
||||
if kind in [object_types.PY_MODULE, object_types.PACKAGE]:
|
||||
|
||||
contents = open(filename, "rt").read()
|
||||
contents = open(filename, 'rt').read()
|
||||
consts = CONSTANT_RE.findall(contents)
|
||||
|
||||
for c in consts:
|
||||
if "," in c:
|
||||
c = c.split(",")
|
||||
if ',' in c:
|
||||
c = c.split(',')
|
||||
constants.extend([v.strip() for v in c])
|
||||
else:
|
||||
constants.append(c.strip())
|
||||
@@ -560,11 +578,11 @@ def ToRest(import_name):
|
||||
pickle_file = os.path.join(sphinxDir, 'wx%s.pkl'%import_name)
|
||||
|
||||
fid = open(pickle_file, 'rb')
|
||||
library_class = cPickle.load(fid)
|
||||
library_class = pickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
fid = open(os.path.join(sphinxDir, 'class_summary.lst'), 'rb')
|
||||
class_summary = cPickle.load(fid)
|
||||
class_summary = pickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
library_class.Walk(library_class, class_summary)
|
||||
@@ -592,33 +610,33 @@ def ModuleHunter(init_name, import_name, version):
|
||||
return
|
||||
|
||||
message = "Importing main library '%s'..."%import_name
|
||||
print "Message: %s"%message
|
||||
print(('Message: %s'%message))
|
||||
|
||||
module_name = os.path.splitext(getfile(mainmod))[0] + ".py"
|
||||
contents = open(module_name, "rt").read()
|
||||
module_name = os.path.splitext(getfile(mainmod))[0] + '.py'
|
||||
contents = open(module_name, 'rt').read()
|
||||
constants = CONSTANT_RE.findall(contents)
|
||||
|
||||
library_class, count = describe_module(mainmod, kind=object_types.LIBRARY, constants=constants)
|
||||
library_class.name = "%s-%s"%(import_name, version)
|
||||
library_class.name = '%s-%s'%(import_name, version)
|
||||
|
||||
message = "Main library '%s' imported..."%library_class.name
|
||||
print "Message: %s"%message
|
||||
print(('Message: %s'%message))
|
||||
|
||||
message = "Importing sub-modules and sub-packages...\n"
|
||||
print "Message: %s"%message
|
||||
print(('Message: %s'%message))
|
||||
|
||||
looped_names = []
|
||||
ancestors_dict = {import_name: library_class}
|
||||
|
||||
for importer, module_name, ispkg in pkgutil.walk_packages(path=[directory],
|
||||
prefix=import_name+".",
|
||||
prefix=import_name+'.',
|
||||
onerror=lambda x: None):
|
||||
|
||||
import_string = module_name
|
||||
splitted = module_name.split(".")
|
||||
splitted = module_name.split('.')
|
||||
|
||||
fromlist = splitted[-1]
|
||||
parent_name = ".".join(splitted[0:-1])
|
||||
parent_name = '.'.join(splitted[0:-1])
|
||||
|
||||
parent_class = ancestors_dict[parent_name]
|
||||
module_class, count = SubImport(import_string, fromlist, parent_class, ispkg)
|
||||
@@ -632,7 +650,7 @@ def ModuleHunter(init_name, import_name, version):
|
||||
ancestors_dict[module_name] = module_class
|
||||
|
||||
major, minor, micro, release = sys.version_info[0:-1]
|
||||
pythonVersion = u"%d.%d.%d-%s"%(major, minor, micro, release)
|
||||
pythonVersion = '%d.%d.%d-%s'%(major, minor, micro, release)
|
||||
|
||||
library_class.python_version = pythonVersion
|
||||
library_class.Save()
|
||||
@@ -640,13 +658,13 @@ def ModuleHunter(init_name, import_name, version):
|
||||
sys.path[:] = path # restore
|
||||
|
||||
fid = open(pickle_file, 'wb')
|
||||
cPickle.dump(library_class, fid)
|
||||
pickle.dump(library_class, fid)
|
||||
fid.close()
|
||||
|
||||
ToRest(import_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
|
||||
argv = sys.argv[1:]
|
||||
|
||||
|
||||
@@ -12,19 +12,24 @@
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import cPickle
|
||||
import glob
|
||||
import random
|
||||
import subprocess
|
||||
|
||||
if sys.version_info < (3,):
|
||||
import cPickle as pickle
|
||||
else:
|
||||
import pickle
|
||||
|
||||
# Phoenix-specific imports
|
||||
import templates
|
||||
from buildtools.config import copyIfNewer, writeIfChanged, newer, getSvnRev, textfile_open
|
||||
|
||||
from utilities import Wx2Sphinx
|
||||
from constants import HTML_REPLACE, TODAY, SPHINXROOT, SECTIONS_EXCLUDE
|
||||
from constants import CONSTANT_INSTANCES, WIDGETS_IMAGES_ROOT, SPHINX_IMAGES_ROOT
|
||||
from . import templates
|
||||
from .utilities import Wx2Sphinx
|
||||
from .constants import HTML_REPLACE, TODAY, SPHINXROOT, SECTIONS_EXCLUDE
|
||||
from .constants import CONSTANT_INSTANCES, WIDGETS_IMAGES_ROOT, SPHINX_IMAGES_ROOT
|
||||
|
||||
|
||||
def MakeHeadings():
|
||||
@@ -94,7 +99,7 @@ def BuildEnumsAndMethods(sphinxDir):
|
||||
"""
|
||||
|
||||
fid = open(os.path.join(sphinxDir, 'class_summary.lst'), 'rb')
|
||||
class_summary = cPickle.load(fid)
|
||||
class_summary = pickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
unreferenced_classes = {}
|
||||
@@ -116,7 +121,7 @@ def BuildEnumsAndMethods(sphinxDir):
|
||||
orig_text = text = fid.read()
|
||||
fid.close()
|
||||
|
||||
for old, new in enum_dict.items():
|
||||
for old, new in list(enum_dict.items()):
|
||||
text = text.replace(old, new)
|
||||
|
||||
widget_name = os.path.split(os.path.splitext(input)[0])[1]
|
||||
@@ -144,6 +149,7 @@ def BuildEnumsAndMethods(sphinxDir):
|
||||
text = text.replace('wx.``', '``')
|
||||
text = text.replace('non-NULL', 'not ``None``')
|
||||
text = text.replace(',,', ',').replace(', ,', ',')
|
||||
text = text.replace('|wx', '|')
|
||||
|
||||
if 'DocstringsGuidelines' not in input:
|
||||
# Leave the DocstringsGuidelines.txt file alone on these ones
|
||||
@@ -182,7 +188,7 @@ def BuildEnumsAndMethods(sphinxDir):
|
||||
'the text file "unreferenced_classes.inc" together with the ReST file names where they\n' \
|
||||
'appear.\n\n'
|
||||
|
||||
keys = unreferenced_classes.keys()
|
||||
keys = list(unreferenced_classes.keys())
|
||||
keys.sort()
|
||||
|
||||
fid = textfile_open(os.path.join(SPHINXROOT, 'unreferenced_classes.inc'), 'wt')
|
||||
@@ -197,7 +203,7 @@ def BuildEnumsAndMethods(sphinxDir):
|
||||
fid.write('='*50 + ' ' + '='*50 + '\n')
|
||||
fid.close()
|
||||
|
||||
print warn%(len(keys))
|
||||
print((warn%(len(keys))))
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
@@ -319,7 +325,7 @@ def ReformatFunctions(file):
|
||||
return
|
||||
|
||||
fid = open(file, 'rb')
|
||||
functions = cPickle.load(fid)
|
||||
functions = pickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
if local_file.count('.') == 1:
|
||||
@@ -328,7 +334,7 @@ def ReformatFunctions(file):
|
||||
else:
|
||||
label = local_file.split('.')[0:-2][0]
|
||||
|
||||
names = functions.keys()
|
||||
names = list(functions.keys())
|
||||
names = [name.lower() for name in names]
|
||||
names.sort()
|
||||
|
||||
@@ -343,7 +349,7 @@ def ReformatFunctions(file):
|
||||
text += ' | '.join([':ref:`%s <%s %s>`'%(letter, label, letter) for letter in letters])
|
||||
text += '\n\n\n'
|
||||
|
||||
names = functions.keys()
|
||||
names = list(functions.keys())
|
||||
names = sorted(names, key=str.lower)
|
||||
|
||||
for letter in letters:
|
||||
@@ -374,7 +380,7 @@ def MakeClassIndex(sphinxDir, file):
|
||||
return
|
||||
|
||||
fid = open(file, 'rb')
|
||||
classes = cPickle.load(fid)
|
||||
classes = pickle.load(fid)
|
||||
fid.close()
|
||||
|
||||
if local_file.count('.') == 1:
|
||||
@@ -390,7 +396,7 @@ def MakeClassIndex(sphinxDir, file):
|
||||
enum_files = glob.glob(sphinxDir + '/%s*.enumeration.txt'%module)
|
||||
enum_base = [os.path.split(os.path.splitext(enum)[0])[1] for enum in enum_files]
|
||||
|
||||
names = classes.keys()
|
||||
names = list(classes.keys())
|
||||
names.sort()
|
||||
|
||||
text = ''
|
||||
@@ -466,7 +472,7 @@ def GenGallery():
|
||||
possible = possible.lower()
|
||||
html_files[possible + '.png'] = simple + '.html'
|
||||
|
||||
keys = html_files.keys()
|
||||
keys = list(html_files.keys())
|
||||
keys.sort()
|
||||
|
||||
text = ''
|
||||
@@ -475,7 +481,7 @@ def GenGallery():
|
||||
possible_png = key
|
||||
html = html_files[possible_png]
|
||||
|
||||
rand_list = range(3)
|
||||
rand_list = list(range(3))
|
||||
random.shuffle(rand_list)
|
||||
|
||||
for plat_index in rand_list:
|
||||
@@ -647,7 +653,7 @@ def PostProcess(folder):
|
||||
|
||||
newtext += line + '\n'
|
||||
|
||||
for old, new in enum_dict.items():
|
||||
for old, new in list(enum_dict.items()):
|
||||
newtext = newtext.replace(old, new)
|
||||
|
||||
newtext = AddJavaScript(newtext)
|
||||
|
||||
@@ -15,18 +15,25 @@ import sys
|
||||
import os
|
||||
import codecs
|
||||
import shutil
|
||||
import glob
|
||||
import imp
|
||||
|
||||
if sys.version_info < (3,):
|
||||
import cPickle as pickle
|
||||
from UserDict import UserDict
|
||||
string_base = basestring
|
||||
else:
|
||||
import pickle
|
||||
from collections import UserDict
|
||||
string_base = str
|
||||
|
||||
# Phoenix-specific imports
|
||||
from buildtools.config import phoenixDir
|
||||
|
||||
from .templates import TEMPLATE_CONTRIB
|
||||
|
||||
from .constants import IGNORE, PUNCTUATION
|
||||
from .constants import CPP_ITEMS, VERSION, VALUE_MAP
|
||||
from .constants import IGNORE, PUNCTUATION, MODULENAME_REPLACE
|
||||
from .constants import CPP_ITEMS, VERSION, VALUE_MAP, NO_MODULE
|
||||
from .constants import RE_KEEP_SPACES, EXTERN_INHERITANCE
|
||||
from .constants import DOXYROOT, SPHINXROOT, WIDGETS_IMAGES_ROOT
|
||||
|
||||
@@ -67,7 +74,7 @@ class odict(UserDict):
|
||||
return dict
|
||||
|
||||
def items(self):
|
||||
return zip(self._keys, self.values())
|
||||
return list(zip(self._keys, list(self.values())))
|
||||
|
||||
def keys(self):
|
||||
return self._keys
|
||||
@@ -89,11 +96,11 @@ class odict(UserDict):
|
||||
|
||||
def update(self, dict):
|
||||
UserDict.update(self, dict)
|
||||
for key in dict.keys():
|
||||
for key in list(dict.keys()):
|
||||
if key not in self._keys: self._keys.append(key)
|
||||
|
||||
def values(self):
|
||||
return map(self.get, self._keys)
|
||||
return list(map(self.get, self._keys))
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
@@ -417,7 +424,7 @@ def FindControlImages(elementOrString):
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(elementOrString, basestring):
|
||||
if isinstance(elementOrString, string_base):
|
||||
class_name = py_class_name = elementOrString.lower()
|
||||
else:
|
||||
element = elementOrString
|
||||
@@ -456,14 +463,14 @@ def FindControlImages(elementOrString):
|
||||
|
||||
appearance[sub_folder] = py_png_file
|
||||
|
||||
if not any(appearance.values()):
|
||||
if not any(list(appearance.values())):
|
||||
return []
|
||||
|
||||
for sub_folder, image in appearance.items():
|
||||
for sub_folder, image in list(appearance.items()):
|
||||
if not image:
|
||||
appearance[sub_folder] = '../no_appearance.png'
|
||||
|
||||
return appearance.values()
|
||||
return list(appearance.values())
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
@@ -627,6 +634,35 @@ def PickleClassInfo(class_name, element, short_description):
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------- #
|
||||
|
||||
global ALL_ITEMS
|
||||
ALL_ITEMS = {}
|
||||
|
||||
def Class2Module():
|
||||
|
||||
global ALL_ITEMS
|
||||
|
||||
if ALL_ITEMS:
|
||||
return ALL_ITEMS
|
||||
|
||||
etg_files = glob.glob(os.path.join(phoenixDir(), 'etg') + '/*.py')
|
||||
etg_files = [files for files in etg_files if not files.startswith('_')]
|
||||
|
||||
for files in etg_files:
|
||||
split = os.path.split(os.path.splitext(files)[0])[1]
|
||||
module = imp.load_source(split, files)
|
||||
|
||||
current_module = MODULENAME_REPLACE.get(module.MODULE, '')
|
||||
|
||||
for item in module.ITEMS:
|
||||
|
||||
item = RemoveWxPrefix(item)
|
||||
ALL_ITEMS[item] = current_module
|
||||
|
||||
ALL_ITEMS.update(NO_MODULE)
|
||||
|
||||
return ALL_ITEMS
|
||||
|
||||
|
||||
def Wx2Sphinx(name):
|
||||
"""
|
||||
@@ -636,13 +672,22 @@ def Wx2Sphinx(name):
|
||||
"""
|
||||
|
||||
if '<' in name:
|
||||
name = name[0:name.index('<')].strip()
|
||||
name = name[0:name.index('<')]
|
||||
|
||||
name = name.strip()
|
||||
newname = fullname = RemoveWxPrefix(name)
|
||||
|
||||
if 'DataView' in name:
|
||||
fullname = 'dataview.' + fullname
|
||||
if '.' in newname:
|
||||
lookup, remainder = newname.split('.')
|
||||
remainder = '.%s'%remainder
|
||||
else:
|
||||
lookup = newname
|
||||
remainder = ''
|
||||
|
||||
all_items = Class2Module()
|
||||
if lookup in all_items:
|
||||
fullname = all_items[lookup] + lookup + remainder
|
||||
|
||||
return newname, fullname
|
||||
|
||||
|
||||
@@ -759,3 +804,9 @@ def FormatExternalLink(fullname, inheritance=False):
|
||||
|
||||
return full_page
|
||||
|
||||
|
||||
def IsPython3():
|
||||
""" Returns ``True`` if we are using Python 3.x. """
|
||||
|
||||
return sys.version_info >= (3, )
|
||||
|
||||
|
||||
Reference in New Issue
Block a user