Make cqlsh PEP8 compliant (and other cleanups)

patch by philipthompson; reviewed by Stefania for CASSANDRA-10066
This commit is contained in:
Philip Thompson 2015-08-31 14:26:57 -04:00 committed by Sylvain Lebresne
parent 7d8663da77
commit 9faa193e9a
13 changed files with 389 additions and 244 deletions

View File

@ -1,4 +1,5 @@
2.1.10
* (cqlsh) Make cqlsh PEP8 compliant (CASSANDRA-10066)
* (cqlsh) Fix error when starting cqlsh with --debug (CASSANDRA-10282)
* Scrub, Cleanup and Upgrade do not unmark compacting until all operations
have completed, regardless of the occurence of exceptions (CASSANDRA-10274)

104
bin/cqlsh
View File

@ -30,31 +30,29 @@ exit 1
":"""
from __future__ import with_statement
import cmd
import codecs
import ConfigParser
import csv
import getpass
import locale
import optparse
import os
import platform
import sys
import time
import traceback
import warnings
from contextlib import contextmanager
from functools import partial
from glob import glob
from StringIO import StringIO
from uuid import UUID
description = "CQL Shell for Apache Cassandra"
version = "5.0.1"
from StringIO import StringIO
from contextlib import contextmanager
from glob import glob
import cmd
import sys
import os
import time
import optparse
import ConfigParser
import codecs
import locale
import platform
import warnings
import csv
import getpass
from functools import partial
import traceback
readline = None
try:
# check if tty first, cause readline doesn't check, and only cares
@ -108,12 +106,12 @@ except ImportError, e:
'Module load path: %r\n\n'
'Error: %s\n' % (sys.executable, sys.path, e))
from cassandra.auth import PlainTextAuthProvider
from cassandra.cluster import Cluster, PagedResult
from cassandra.query import SimpleStatement, ordered_dict_factory
from cassandra.metadata import protect_name, protect_names, protect_value
from cassandra.policies import WhiteListRoundRobinPolicy
from cassandra.protocol import QueryMessage, ResultMessage
from cassandra.metadata import protect_name, protect_names, protect_value
from cassandra.auth import PlainTextAuthProvider
from cassandra.query import SimpleStatement, ordered_dict_factory
# cqlsh should run correctly when run out of a Cassandra source tree,
# out of an unpacked Cassandra tarball, and after a proper package install.
@ -121,12 +119,13 @@ cqlshlibdir = os.path.join(CASSANDRA_PATH, 'pylib')
if os.path.isdir(cqlshlibdir):
sys.path.insert(0, cqlshlibdir)
from cqlshlib import cqlhandling, cql3handling, pylexotron, sslhandling
from cqlshlib.displaying import (RED, BLUE, CYAN, ANSI_RESET, COLUMN_NAME_COLORS,
FormattedValue, colorme)
from cqlshlib.formatting import format_by_type, formatter_for, format_value_utype
from cqlshlib.util import trim_if_present, get_file_encoding_bomsize
from cqlshlib.tracing import print_trace_session, print_trace
from cqlshlib import cql3handling, cqlhandling, pylexotron, sslhandling
from cqlshlib.displaying import (ANSI_RESET, BLUE, COLUMN_NAME_COLORS, CYAN,
RED, FormattedValue, colorme)
from cqlshlib.formatting import (format_by_type, format_value_utype,
formatter_for)
from cqlshlib.tracing import print_trace, print_trace_session
from cqlshlib.util import get_file_encoding_bomsize, trim_if_present
DEFAULT_HOST = '127.0.0.1'
DEFAULT_PORT = 9042
@ -179,7 +178,7 @@ parser.add_option("--connect-timeout", default=DEFAULT_CONNECT_TIMEOUT_SECONDS,
optvalues = optparse.Values()
(options, arguments) = parser.parse_args(sys.argv[1:], values=optvalues)
#BEGIN history/config definition
# BEGIN history/config definition
HISTORY_DIR = os.path.expanduser(os.path.join('~', '.cassandra'))
if hasattr(options, 'cqlshrc'):
@ -210,7 +209,7 @@ if os.path.exists(OLD_CONFIG_FILE):
OLD_HISTORY = os.path.expanduser(os.path.join('~', '.cqlsh_history'))
if os.path.exists(OLD_HISTORY):
os.rename(OLD_HISTORY, HISTORY)
#END history/config definition
# END history/config definition
CQL_ERRORS = (
cassandra.AlreadyExists, cassandra.AuthenticationFailed, cassandra.InvalidRequest,
@ -443,12 +442,15 @@ class KeyspaceNotFound(Exception):
class ColumnFamilyNotFound(Exception):
pass
class IndexNotFound(Exception):
pass
class ObjectNotFound(Exception):
pass
class VersionNotSupported(Exception):
pass
@ -668,7 +670,7 @@ class Shell(cmd.Cmd):
self.stdin = stdin
self.query_out = sys.stdout
self.consistency_level = cassandra.ConsistencyLevel.ONE
self.serial_consistency_level = cassandra.ConsistencyLevel.SERIAL;
self.serial_consistency_level = cassandra.ConsistencyLevel.SERIAL
# the python driver returns BLOBs as string, but we expect them as bytearrays
cassandra.cqltypes.BytesType.deserialize = staticmethod(lambda byts, protocol_version: bytearray(byts))
cassandra.cqltypes.CassandraType.support_empty_values = True
@ -681,7 +683,7 @@ class Shell(cmd.Cmd):
def refresh_schema_metadata_best_effort(self):
try:
self.conn.refresh_schema_metadata(5) #will throw exception if there is a schema mismatch
self.conn.refresh_schema_metadata(5) # will throw exception if there is a schema mismatch
except Exception:
self.printerr("Warning: schema version mismatch detected, which might be caused by DOWN nodes; if "
"this is not the case, check the schema versions of your nodes in system.local and "
@ -724,9 +726,9 @@ class Shell(cmd.Cmd):
def show_host(self):
print "Connected to %s at %s:%d." % \
(self.applycolor(self.get_cluster_name(), BLUE),
self.hostname,
self.port)
(self.applycolor(self.get_cluster_name(), BLUE),
self.hostname,
self.port)
def show_version(self):
vers = self.connection_versions.copy()
@ -796,7 +798,7 @@ class Shell(cmd.Cmd):
return self.conn.metadata.partitioner
def get_keyspace_meta(self, ksname):
if not ksname in self.conn.metadata.keyspaces:
if ksname not in self.conn.metadata.keyspaces:
raise KeyspaceNotFound('Keyspace %r not found.' % ksname)
return self.conn.metadata.keyspaces[ksname]
@ -1361,10 +1363,9 @@ class Shell(cmd.Cmd):
p = trim_if_present(self.get_partitioner(), 'org.apache.cassandra.dht.')
print 'Partitioner: %s\n' % p
# TODO: snitch?
#snitch = trim_if_present(self.get_snitch(), 'org.apache.cassandra.locator.')
#print 'Snitch: %s\n' % snitch
if self.current_keyspace is not None \
and self.current_keyspace != 'system':
# snitch = trim_if_present(self.get_snitch(), 'org.apache.cassandra.locator.')
# print 'Snitch: %s\n' % snitch
if self.current_keyspace is not None and self.current_keyspace != 'system':
print "Range ownership:"
ring = self.get_ring()
for entry in ring.items():
@ -1374,7 +1375,7 @@ class Shell(cmd.Cmd):
def describe_schema(self, include_system=False):
print
for k in self.get_keyspaces():
if include_system or not k.name in SYSTEM_KEYSPACES:
if include_system or k.name not in SYSTEM_KEYSPACES:
self.print_recreate_keyspace(k, sys.stdout)
print
@ -1682,15 +1683,15 @@ class Shell(cmd.Cmd):
# open a new connection for this subprocess
new_cluster = Cluster(
contact_points=(self.hostname,),
port=self.port,
cql_version=self.conn.cql_version,
protocol_version=DEFAULT_PROTOCOL_VERSION,
auth_provider=self.auth_provider,
ssl_options=sslhandling.ssl_settings(self.hostname, CONFIG_FILE) if self.ssl else None,
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]),
compression=None,
connect_timeout=self.conn.connect_timeout)
contact_points=(self.hostname,),
port=self.port,
cql_version=self.conn.cql_version,
protocol_version=DEFAULT_PROTOCOL_VERSION,
auth_provider=self.auth_provider,
ssl_options=sslhandling.ssl_settings(self.hostname, CONFIG_FILE) if self.ssl else None,
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]),
compression=None,
connect_timeout=self.conn.connect_timeout)
session = new_cluster.connect(self.keyspace)
conn = session._pools.values()[0]._connection
@ -2282,6 +2283,7 @@ def option_with_default(cparser_getter, section, option, default=None):
except ConfigParser.Error:
return default
def raw_option_with_default(configs, section, option, default=None):
"""
Same (almost) as option_with_default() but won't do any string interpolation.

View File

@ -13,4 +13,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

View File

@ -16,7 +16,6 @@
from .cqlhandling import CqlParsingRuleSet, Hint
from cassandra.metadata import maybe_escape_name
from cassandra.metadata import escape_name
simple_cql_types = set(('ascii', 'bigint', 'blob', 'boolean', 'counter', 'decimal', 'double', 'float', 'inet', 'int',
@ -26,7 +25,9 @@ simple_cql_types.difference_update(('set', 'map', 'list'))
from . import helptopics
cqldocs = helptopics.CQL3HelpTopics()
class UnexpectedTableStructure(UserWarning):
def __init__(self, msg):
self.msg = msg
@ -36,6 +37,7 @@ class UnexpectedTableStructure(UserWarning):
SYSTEM_KEYSPACES = ('system', 'system_traces', 'system_auth')
NONALTERBALE_KEYSPACES = ('system')
class Cql3ParsingRuleSet(CqlParsingRuleSet):
columnfamily_layout_options = (
@ -79,7 +81,7 @@ class Cql3ParsingRuleSet(CqlParsingRuleSet):
@classmethod
def escape_value(cls, value):
if value is None:
return 'NULL' # this totally won't work
return 'NULL' # this totally won't work
if isinstance(value, bool):
value = str(value).lower()
elif isinstance(value, float):
@ -291,6 +293,7 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
'''
def prop_equals_completer(ctxt, cass):
if not working_on_keyspace(ctxt):
# we know if the thing in the property name position is "compact" or
@ -305,6 +308,7 @@ def prop_equals_completer(ctxt, cass):
completer_for('property', 'propeq')(prop_equals_completer)
@completer_for('property', 'propname')
def prop_name_completer(ctxt, cass):
if working_on_keyspace(ctxt):
@ -312,6 +316,7 @@ def prop_name_completer(ctxt, cass):
else:
return cf_prop_name_completer(ctxt, cass)
@completer_for('propertyValue', 'propsimpleval')
def prop_val_completer(ctxt, cass):
if working_on_keyspace(ctxt):
@ -319,6 +324,7 @@ def prop_val_completer(ctxt, cass):
else:
return cf_prop_val_completer(ctxt, cass)
@completer_for('propertyValue', 'propmapkey')
def prop_val_mapkey_completer(ctxt, cass):
if working_on_keyspace(ctxt):
@ -326,6 +332,7 @@ def prop_val_mapkey_completer(ctxt, cass):
else:
return cf_prop_val_mapkey_completer(ctxt, cass)
@completer_for('propertyValue', 'propmapval')
def prop_val_mapval_completer(ctxt, cass):
if working_on_keyspace(ctxt):
@ -333,6 +340,7 @@ def prop_val_mapval_completer(ctxt, cass):
else:
return cf_prop_val_mapval_completer(ctxt, cass)
@completer_for('propertyValue', 'ender')
def prop_val_mapender_completer(ctxt, cass):
if working_on_keyspace(ctxt):
@ -340,12 +348,14 @@ def prop_val_mapender_completer(ctxt, cass):
else:
return cf_prop_val_mapender_completer(ctxt, cass)
def ks_prop_name_completer(ctxt, cass):
optsseen = ctxt.get_binding('propname', ())
if 'replication' not in optsseen:
return ['replication']
return ["durable_writes"]
def ks_prop_val_completer(ctxt, cass):
optname = ctxt.get_binding('propname')[-1]
if optname == 'durable_writes':
@ -354,6 +364,7 @@ def ks_prop_val_completer(ctxt, cass):
return ["{'class': '"]
return ()
def ks_prop_val_mapkey_completer(ctxt, cass):
optname = ctxt.get_binding('propname')[-1]
if optname != 'replication':
@ -372,6 +383,7 @@ def ks_prop_val_mapkey_completer(ctxt, cass):
return [Hint('<dc_name>')]
return map(escape_value, opts.difference(keysseen))
def ks_prop_val_mapval_completer(ctxt, cass):
optname = ctxt.get_binding('propname')[-1]
if optname != 'replication':
@ -381,6 +393,7 @@ def ks_prop_val_mapval_completer(ctxt, cass):
return map(escape_value, CqlRuleSet.replication_strategies)
return [Hint('<term>')]
def ks_prop_val_mapender_completer(ctxt, cass):
optname = ctxt.get_binding('propname')[-1]
if optname != 'replication':
@ -400,10 +413,12 @@ def ks_prop_val_mapender_completer(ctxt, cass):
return [',']
return ['}']
def cf_prop_name_completer(ctxt, cass):
return [c[0] for c in (CqlRuleSet.columnfamily_layout_options +
CqlRuleSet.columnfamily_layout_map_options)]
def cf_prop_val_completer(ctxt, cass):
exist_opts = ctxt.get_binding('propname')
this_opt = exist_opts[-1]
@ -423,6 +438,7 @@ def cf_prop_val_completer(ctxt, cass):
return [Hint('<integer>')]
return [Hint('<option_value>')]
def cf_prop_val_mapkey_completer(ctxt, cass):
optname = ctxt.get_binding('propname')[-1]
for cql3option, _, subopts in CqlRuleSet.columnfamily_layout_map_options:
@ -459,6 +475,7 @@ def cf_prop_val_mapkey_completer(ctxt, cass):
return map(escape_value, opts)
return ()
def cf_prop_val_mapval_completer(ctxt, cass):
opt = ctxt.get_binding('propname')[-1]
key = dequote_value(ctxt.get_binding('propmapkey')[-1])
@ -477,36 +494,44 @@ def cf_prop_val_mapval_completer(ctxt, cass):
return ["'ALL'", "'NONE'"]
return ()
def cf_prop_val_mapender_completer(ctxt, cass):
return [',', '}']
@completer_for('tokenDefinition', 'token')
def token_word_completer(ctxt, cass):
return ['token(']
@completer_for('simpleStorageType', 'typename')
def storagetype_completer(ctxt, cass):
return simple_cql_types
@completer_for('keyspaceName', 'ksname')
def ks_name_completer(ctxt, cass):
return map(maybe_escape_name, cass.get_keyspace_names())
@completer_for('nonSystemKeyspaceName', 'ksname')
def ks_name_completer(ctxt, cass):
ksnames = [n for n in cass.get_keyspace_names() if n not in SYSTEM_KEYSPACES]
return map(maybe_escape_name, ksnames)
@completer_for('alterableKeyspaceName', 'ksname')
def ks_name_completer(ctxt, cass):
ksnames = [n for n in cass.get_keyspace_names() if n not in NONALTERBALE_KEYSPACES]
return map(maybe_escape_name, ksnames)
def cf_ks_name_completer(ctxt, cass):
return [maybe_escape_name(ks) + '.' for ks in cass.get_keyspace_names()]
completer_for('columnFamilyName', 'ksname')(cf_ks_name_completer)
def cf_ks_dot_completer(ctxt, cass):
name = dequote_name(ctxt.get_binding('ksname'))
if name in cass.get_keyspace_names():
@ -515,6 +540,7 @@ def cf_ks_dot_completer(ctxt, cass):
completer_for('columnFamilyName', 'dot')(cf_ks_dot_completer)
@completer_for('columnFamilyName', 'cfname')
def cf_name_completer(ctxt, cass):
ks = ctxt.get_binding('ksname', None)
@ -532,6 +558,7 @@ completer_for('userTypeName', 'ksname')(cf_ks_name_completer)
completer_for('userTypeName', 'dot')(cf_ks_dot_completer)
def ut_name_completer(ctxt, cass):
ks = ctxt.get_binding('ksname', None)
if ks is not None:
@ -548,6 +575,7 @@ def ut_name_completer(ctxt, cass):
completer_for('userTypeName', 'utname')(ut_name_completer)
completer_for('userType', 'utname')(ut_name_completer)
@completer_for('unreservedKeyword', 'nocomplete')
def unreserved_keyword_completer(ctxt, cass):
# we never want to provide completions through this production;
@ -555,6 +583,7 @@ def unreserved_keyword_completer(ctxt, cass):
# names, CF names, property values, etc.
return ()
def get_table_meta(ctxt, cass):
ks = ctxt.get_binding('ksname', None)
if ks is not None:
@ -562,6 +591,7 @@ def get_table_meta(ctxt, cass):
cf = dequote_name(ctxt.get_binding('cfname'))
return cass.get_table_meta(ks, cf)
def get_ut_layout(ctxt, cass):
ks = ctxt.get_binding('ksname', None)
if ks is not None:
@ -569,6 +599,7 @@ def get_ut_layout(ctxt, cass):
ut = dequote_name(ctxt.get_binding('utname'))
return cass.get_usertype_layout(ks, ut)
def working_on_keyspace(ctxt):
wat = ctxt.get_binding('wat').upper()
if wat in ('KEYSPACE', 'SCHEMA'):
@ -611,6 +642,7 @@ syntax_rules += r'''
;
'''
@completer_for('orderByClause', 'ordercol')
def select_order_column_completer(ctxt, cass):
prev_order_cols = ctxt.get_binding('ordercol', ())
@ -625,15 +657,18 @@ def select_order_column_completer(ctxt, cass):
return [maybe_escape_name(order_by_candidates[len(prev_order_cols)])]
return [Hint('No more orderable columns here.')]
@completer_for('relation', 'token')
def relation_token_word_completer(ctxt, cass):
return ['TOKEN(']
@completer_for('relation', 'rel_tokname')
def relation_token_subject_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
return [key.name for key in layout.partition_key]
@completer_for('relation', 'rel_lhs')
def select_relation_lhs_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -654,6 +689,7 @@ def select_relation_lhs_completer(ctxt, cass):
filterable.add(cd.name)
return map(maybe_escape_name, filterable)
@completer_for('selectClause', 'star')
def select_count_star_completer(ctxt, cass):
return ['*']
@ -675,13 +711,15 @@ syntax_rules += r'''
;
'''
def regular_column_names(table_meta):
if not table_meta or not table_meta.columns:
return []
regular_coulmns = list(set(table_meta.columns.keys())
- set([key.name for key in table_meta.partition_key])
- set([key.name for key in table_meta.clustering_key]))
return regular_coulmns
regular_columns = list(set(table_meta.columns.keys()) -
set([key.name for key in table_meta.partition_key]) -
set([key.name for key in table_meta.clustering_key]))
return regular_columns
@completer_for('insertStatement', 'colname')
def insert_colname_completer(ctxt, cass):
@ -694,6 +732,7 @@ def insert_colname_completer(ctxt, cass):
normalcols = set(regular_column_names(layout)) - colnames
return map(maybe_escape_name, normalcols)
@completer_for('insertStatement', 'newval')
def insert_newval_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -713,6 +752,7 @@ def insert_newval_completer(ctxt, cass):
return [Hint('<value for %s (%s)>' % (maybe_escape_name(curcol),
cqltype.cql_parameterized_type()))]
@completer_for('insertStatement', 'valcomma')
def insert_valcomma_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -722,6 +762,7 @@ def insert_valcomma_completer(ctxt, cass):
return [',']
return [')']
@completer_for('insertStatement', 'insertopt')
def insert_option_completer(ctxt, cass):
opts = set('TIMESTAMP TTL'.split())
@ -750,6 +791,7 @@ syntax_rules += r'''
;
'''
@completer_for('updateStatement', 'updateopt')
def insert_option_completer(ctxt, cass):
opts = set('TIMESTAMP TTL'.split())
@ -757,11 +799,13 @@ def insert_option_completer(ctxt, cass):
opts.discard(opt.split()[0])
return opts
@completer_for('assignment', 'updatecol')
def update_col_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
return map(maybe_escape_name, regular_column_names(layout))
@completer_for('assignment', 'update_rhs')
def update_countername_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -776,12 +820,14 @@ def update_countername_completer(ctxt, cass):
return ["["]
return [Hint('<term (%s)>' % cqltype.cql_parameterized_type())]
@completer_for('assignment', 'counterop')
def update_counterop_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
curcol = dequote_name(ctxt.get_binding('updatecol', ''))
return ['+', '-'] if layout.columns[curcol].data_type.typename == 'counter' else []
@completer_for('assignment', 'inc')
def update_counter_inc_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -790,6 +836,7 @@ def update_counter_inc_completer(ctxt, cass):
return [Hint('<wholenumber>')]
return []
@completer_for('assignment', 'listadder')
def update_listadder_completer(ctxt, cass):
rhs = ctxt.get_binding('update_rhs')
@ -797,6 +844,7 @@ def update_listadder_completer(ctxt, cass):
return ['+']
return []
@completer_for('assignment', 'listcol')
def update_listcol_completer(ctxt, cass):
rhs = ctxt.get_binding('update_rhs')
@ -805,6 +853,7 @@ def update_listcol_completer(ctxt, cass):
return [maybe_escape_name(colname)]
return []
@completer_for('assignment', 'indexbracket')
def update_indexbracket_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -827,6 +876,7 @@ syntax_rules += r'''
;
'''
@completer_for('deleteStatement', 'delopt')
def delete_opt_completer(ctxt, cass):
opts = set('TIMESTAMP'.split())
@ -834,6 +884,7 @@ def delete_opt_completer(ctxt, cass):
opts.discard(opt.split()[0])
return opts
@completer_for('deleteSelector', 'delcol')
def delete_delcol_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -853,6 +904,7 @@ syntax_rules += r'''
;
'''
@completer_for('batchStatement', 'batchopt')
def batch_opt_completer(ctxt, cass):
opts = set('TIMESTAMP'.split())
@ -871,6 +923,7 @@ syntax_rules += r'''
;
'''
@completer_for('createKeyspaceStatement', 'wat')
def create_ks_wat_completer(ctxt, cass):
# would prefer to get rid of the "schema" nomenclature in cql3
@ -910,6 +963,7 @@ syntax_rules += r'''
;
'''
@completer_for('cfamOrdering', 'ordercol')
def create_cf_clustering_order_colname_completer(ctxt, cass):
colnames = map(dequote_name, ctxt.get_binding('newcolname', ()))
@ -917,6 +971,7 @@ def create_cf_clustering_order_colname_completer(ctxt, cass):
# precisely which are. This is good enough for now
return colnames
@completer_for('createColumnFamilyStatement', 'wat')
def create_cf_wat_completer(ctxt, cass):
# would prefer to get rid of the "columnfamily" nomenclature in cql3
@ -927,6 +982,7 @@ def create_cf_wat_completer(ctxt, cass):
explain_completion('createColumnFamilyStatement', 'cf', '<new_table_name>')
explain_completion('compositeKeyCfSpec', 'newcolname', '<new_column_name>')
@completer_for('createColumnFamilyStatement', 'dot')
def create_cf_ks_dot_completer(ctxt, cass):
ks = dequote_name(ctxt.get_binding('ks'))
@ -934,6 +990,7 @@ def create_cf_ks_dot_completer(ctxt, cass):
return ['.']
return []
@completer_for('pkDef', 'ptkey')
def create_cf_pkdef_declaration_completer(ctxt, cass):
cols_declared = ctxt.get_binding('newcolname')
@ -945,6 +1002,7 @@ def create_cf_pkdef_declaration_completer(ctxt, cass):
return ()
return [maybe_escape_name(cols_declared[0])]
@completer_for('compositeKeyCfSpec', 'pkey')
def create_cf_composite_key_declaration_completer(ctxt, cass):
cols_declared = ctxt.get_binding('newcolname')
@ -956,14 +1014,17 @@ def create_cf_composite_key_declaration_completer(ctxt, cass):
return ()
return [maybe_escape_name(cols_declared[0])]
@completer_for('compositeKeyCfSpec', 'k')
def create_cf_composite_primary_key_keyword_completer(ctxt, cass):
return ['KEY (']
@completer_for('compositeKeyCfSpec', 'p')
def create_cf_composite_primary_key_paren_completer(ctxt, cass):
return ['(']
@completer_for('compositeKeyCfSpec', 'c')
def create_cf_composite_primary_key_comma_completer(ctxt, cass):
cols_declared = ctxt.get_binding('newcolname')
@ -997,6 +1058,7 @@ explain_completion('createIndexStatement', 'indexname', '<new_index_name>')
explain_completion('createUserTypeStatement', 'typename', '<new_type_name>')
explain_completion('createUserTypeStatement', 'newcol', '<new_field_name>')
@completer_for('createIndexStatement', 'col')
def create_index_col_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
@ -1024,10 +1086,12 @@ syntax_rules += r'''
'''
@completer_for('indexName', 'ksname')
def idx_ks_name_completer(ctxt, cass):
return [maybe_escape_name(ks) + '.' for ks in cass.get_keyspace_names()]
@completer_for('indexName', 'dot')
def idx_ks_dot_completer(ctxt, cass):
name = dequote_name(ctxt.get_binding('ksname'))
@ -1035,6 +1099,7 @@ def idx_ks_dot_completer(ctxt, cass):
return ['.']
return []
@completer_for('indexName', 'idxname')
def idx_ks_idx_name_completer(ctxt, cass):
ks = ctxt.get_binding('ksname', None)
@ -1070,12 +1135,14 @@ syntax_rules += r'''
;
'''
@completer_for('alterInstructions', 'existcol')
def alter_table_col_completer(ctxt, cass):
layout = get_table_meta(ctxt, cass)
cols = [str(md) for md in layout.columns]
return map(maybe_escape_name, cols)
@completer_for('alterTypeInstructions', 'existcol')
def alter_type_field_completer(ctxt, cass):
layout = get_ut_layout(ctxt, cass)
@ -1145,6 +1212,7 @@ syntax_rules += r'''
;
'''
@completer_for('username', 'name')
def username_name_completer(ctxt, cass):
def maybe_quote(name):
@ -1170,12 +1238,14 @@ syntax_rules += r'''
'''
explain_completion('createTriggerStatement', 'class', '\'fully qualified class name\'')
def get_trigger_names(ctxt, cass):
ks = ctxt.get_binding('ksname', None)
if ks is not None:
ks = dequote_name(ks)
return cass.get_trigger_names(ks)
@completer_for('dropTriggerStatement', 'triggername')
def alter_type_field_completer(ctxt, cass):
names = get_trigger_names(ctxt, cass)

View File

@ -210,17 +210,17 @@ class CqlParsingRuleSet(pylexotron.ParsingRuleSet):
newcandidates = []
for c in candidates:
if self.want_space_between(tokens[-1], c) \
and prefix is None \
and not text[-1].isspace() \
and not c[0].isspace():
and prefix is None \
and not text[-1].isspace() \
and not c[0].isspace():
c = ' ' + c
newcandidates.append(c)
candidates = newcandidates
# append a space for single, complete identifiers
if len(candidates) == 1 and candidates[0][-1].isalnum() \
and lasttype != 'unclosedString' \
and lasttype != 'unclosedName':
and lasttype != 'unclosedString' \
and lasttype != 'unclosedName':
candidates[0] += ' '
return candidates, hints

View File

@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import re
from collections import defaultdict
RED = '\033[0;1;31m'
@ -27,12 +26,15 @@ WHITE = '\033[0;1;37m'
DARK_MAGENTA = '\033[0;35m'
ANSI_RESET = '\033[0m'
def colorme(bval, colormap, colorkey):
if colormap is None:
colormap = DEFAULT_VALUE_COLORS
return FormattedValue(bval, colormap[colorkey] + bval + colormap['reset'])
class FormattedValue:
def __init__(self, strval, coloredval=None, displaywidth=None):
self.strval = strval
if coloredval is None:
@ -106,7 +108,7 @@ DEFAULT_VALUE_COLORS = dict(
)
COLUMN_NAME_COLORS = defaultdict(lambda: MAGENTA,
error=RED,
blob=DARK_MAGENTA,
reset=ANSI_RESET,
)
error=RED,
blob=DARK_MAGENTA,
reset=ANSI_RESET,
)

View File

@ -16,7 +16,6 @@
import sys
import re
import time
import calendar
import math
from collections import defaultdict
@ -29,6 +28,7 @@ from util import UTC
unicode_controlchars_re = re.compile(r'[\x00-\x31\x7f-\xa0]')
controlchars_re = re.compile(r'[\x00-\x31\x7f-\xff]')
def _show_control_chars(match):
txt = repr(match.group(0))
if txt.startswith('u'):
@ -39,6 +39,7 @@ def _show_control_chars(match):
bits_to_turn_red_re = re.compile(r'\\([^uUx]|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{2}|U[0-9a-fA-F]{8})')
def _make_turn_bits_red_f(color1, color2):
def _turn_bits_red(match):
txt = match.group(0)
@ -53,6 +54,7 @@ default_float_precision = 3
default_colormap = DEFAULT_VALUE_COLORS
empty_colormap = defaultdict(lambda: '')
def format_by_type(cqltype, val, encoding, colormap=None, addcolor=False,
nullval=None, time_format=None, float_precision=None):
if nullval is None:
@ -71,6 +73,7 @@ def format_by_type(cqltype, val, encoding, colormap=None, addcolor=False,
time_format=time_format, float_precision=float_precision,
nullval=nullval)
def color_text(bval, colormap, displaywidth=None):
# note that here, we render natural backslashes as just backslashes,
# in the same color as surrounding text, when using color. When not
@ -89,6 +92,7 @@ def color_text(bval, colormap, displaywidth=None):
displaywidth -= bval.count(r'\\')
return FormattedValue(bval, coloredval, displaywidth)
def format_value_default(val, colormap, **_):
val = str(val)
escapedval = val.replace('\\', '\\\\')
@ -99,18 +103,21 @@ def format_value_default(val, colormap, **_):
# making format_value a generic function
_formatters = {}
def format_value(type, val, **kwargs):
if val == EMPTY:
return format_value_default('', **kwargs)
formatter = _formatters.get(type.__name__, format_value_default)
return formatter(val, **kwargs)
def formatter_for(typname):
def registrator(f):
_formatters[typname] = f
return f
return registrator
@formatter_for('bytearray')
def format_value_blob(val, colormap, **_):
bval = '0x' + ''.join('%02x' % c for c in val)
@ -124,10 +131,12 @@ def format_python_formatted_type(val, colormap, color, quote=False):
bval = "'%s'" % bval
return colorme(bval, colormap, color)
@formatter_for('Decimal')
def format_value_decimal(val, colormap, **_):
return format_python_formatted_type(val, colormap, 'decimal')
@formatter_for('UUID')
def format_value_uuid(val, colormap, **_):
return format_python_formatted_type(val, colormap, 'uuid')
@ -137,17 +146,19 @@ def format_value_uuid(val, colormap, **_):
def formatter_value_inet(val, colormap, quote=False, **_):
return format_python_formatted_type(val, colormap, 'inet', quote=quote)
@formatter_for('bool')
def format_value_boolean(val, colormap, **_):
return format_python_formatted_type(val, colormap, 'boolean')
def format_floating_point_type(val, colormap, float_precision, **_):
if math.isnan(val):
bval = 'NaN'
elif math.isinf(val):
bval = 'Infinity'
else:
exponent = int(math.log10(abs(val))) if abs(val) > sys.float_info.epsilon else -sys.maxint -1
exponent = int(math.log10(abs(val))) if abs(val) > sys.float_info.epsilon else -sys.maxint - 1
if -4 <= exponent < float_precision:
# when this is true %g will not use scientific notation,
# increasing precision should not change this decision
@ -159,6 +170,7 @@ def format_floating_point_type(val, colormap, float_precision, **_):
formatter_for('float')(format_floating_point_type)
def format_integer_type(val, colormap, **_):
# base-10 only for now; support others?
bval = str(val)
@ -167,6 +179,7 @@ def format_integer_type(val, colormap, **_):
formatter_for('long')(format_integer_type)
formatter_for('int')(format_integer_type)
@formatter_for('date')
def format_value_timestamp(val, colormap, time_format, quote=False, **_):
bval = strftime(time_format, calendar.timegm(val.utctimetuple()))
@ -176,10 +189,12 @@ def format_value_timestamp(val, colormap, time_format, quote=False, **_):
formatter_for('datetime')(format_value_timestamp)
def strftime(time_format, seconds):
tzless_dt = datetime_from_timestamp(seconds)
return tzless_dt.replace(tzinfo=UTC()).strftime(time_format)
@formatter_for('str')
def format_value_text(val, encoding, colormap, quote=False, **_):
escapedval = val.replace(u'\\', u'\\\\')
@ -195,6 +210,7 @@ def format_value_text(val, encoding, colormap, quote=False, **_):
# name alias
formatter_for('unicode')(format_value_text)
def format_simple_collection(val, lbracket, rbracket, encoding,
colormap, time_format, float_precision, nullval):
subs = [format_value(type(sval), sval, encoding=encoding, colormap=colormap,
@ -208,16 +224,19 @@ def format_simple_collection(val, lbracket, rbracket, encoding,
displaywidth = 2 * len(subs) + sum(sval.displaywidth for sval in subs)
return FormattedValue(bval, coloredval, displaywidth)
@formatter_for('list')
def format_value_list(val, encoding, colormap, time_format, float_precision, nullval, **_):
return format_simple_collection(val, '[', ']', encoding, colormap,
time_format, float_precision, nullval)
@formatter_for('tuple')
def format_value_tuple(val, encoding, colormap, time_format, float_precision, nullval, **_):
return format_simple_collection(val, '(', ')', encoding, colormap,
time_format, float_precision, nullval)
@formatter_for('set')
def format_value_set(val, encoding, colormap, time_format, float_precision, nullval, **_):
return format_simple_collection(sorted(val), '{', '}', encoding, colormap,
@ -238,8 +257,8 @@ def format_value_map(val, encoding, colormap, time_format, float_precision, null
lb, comma, colon, rb = [colormap['collection'] + s + colormap['reset']
for s in ('{', ', ', ': ', '}')]
coloredval = lb \
+ comma.join(k.coloredval + colon + v.coloredval for (k, v) in subs) \
+ rb
+ comma.join(k.coloredval + colon + v.coloredval for (k, v) in subs) \
+ rb
displaywidth = 4 * len(subs) + sum(k.displaywidth + v.displaywidth for (k, v) in subs)
return FormattedValue(bval, coloredval, displaywidth)
formatter_for('OrderedDict')(format_value_map)
@ -263,7 +282,7 @@ def format_value_utype(val, encoding, colormap, time_format, float_precision, nu
lb, comma, colon, rb = [colormap['collection'] + s + colormap['reset']
for s in ('{', ', ', ': ', '}')]
coloredval = lb \
+ comma.join(k.coloredval + colon + v.coloredval for (k, v) in subs) \
+ rb
+ comma.join(k.coloredval + colon + v.coloredval for (k, v) in subs) \
+ rb
displaywidth = 4 * len(subs) + sum(k.displaywidth + v.displaywidth for (k, v) in subs)
return FormattedValue(bval, coloredval, displaywidth)

View File

@ -16,9 +16,11 @@
from .cql3handling import simple_cql_types
class CQLHelpTopics(object):
def get_help_topics(self):
return [ t[5:] for t in dir(self) if t.startswith('help_') ]
return [t[5:] for t in dir(self) if t.startswith('help_')]
def print_help_topic(self, topic):
getattr(self, 'help_' + topic.lower())()
@ -454,7 +456,9 @@ class CQLHelpTopics(object):
unset.
"""
class CQL3HelpTopics(CQLHelpTopics):
def help_create_keyspace(self):
print """
CREATE KEYSPACE <ksname>

View File

@ -17,7 +17,9 @@
import re
from .saferscanner import SaferScanner
class LexingError(Exception):
@classmethod
def from_text(cls, rulestr, unmatched, msg='Lexing error'):
bad_char = len(rulestr) - len(unmatched)
@ -37,7 +39,9 @@ class LexingError(Exception):
def __str__(self):
return '%s at line %d, char %d' % (self.msg, self.linenum, self.charnum)
class Hint:
def __init__(self, text):
self.text = text
@ -50,9 +54,11 @@ class Hint:
def __repr__(self):
return '%s(%r)' % (self.__class__, self.text)
def is_hint(x):
return isinstance(x, Hint)
class ParseContext:
"""
These are meant to be immutable, although it would be something of a
@ -116,7 +122,9 @@ class ParseContext:
return '<%s matched=%r remainder=%r prodname=%r bindings=%r>' \
% (self.__class__.__name__, self.matched, self.remainder, self.productionname, self.bindings)
class matcher:
def __init__(self, arg):
self.arg = arg
@ -154,7 +162,9 @@ class matcher:
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.arg)
class choice(matcher):
def match(self, ctxt, completions):
foundctxts = []
for a in self.arg:
@ -162,11 +172,15 @@ class choice(matcher):
foundctxts.extend(subctxts)
return foundctxts
class one_or_none(matcher):
def match(self, ctxt, completions):
return [ctxt] + list(self.arg.match(ctxt, completions))
class repeat(matcher):
def match(self, ctxt, completions):
found = [ctxt]
ctxts = [ctxt]
@ -179,7 +193,9 @@ class repeat(matcher):
found.extend(new_ctxts)
ctxts = new_ctxts
class rule_reference(matcher):
def match(self, ctxt, completions):
prevname = ctxt.productionname
try:
@ -189,7 +205,9 @@ class rule_reference(matcher):
output = rule.match(ctxt.with_production_named(self.arg), completions)
return [c.with_production_named(prevname) for c in output]
class rule_series(matcher):
def match(self, ctxt, completions):
ctxts = [ctxt]
for patpiece in self.arg:
@ -201,7 +219,9 @@ class rule_series(matcher):
ctxts = new_ctxts
return ctxts
class named_symbol(matcher):
def __init__(self, name, arg):
matcher.__init__(self, arg)
self.name = name
@ -217,7 +237,9 @@ class named_symbol(matcher):
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.arg)
class named_collector(named_symbol):
def match(self, ctxt, completions):
pass_in_compls = completions
if self.try_registered_completion(ctxt, self.name, completions):
@ -229,11 +251,15 @@ class named_collector(named_symbol):
output.append(ctxt.with_binding(self.name, oldval + (ctxt.extract_orig(matchtoks),)))
return output
class terminal_matcher(matcher):
def pattern(self):
raise NotImplementedError
class regex_rule(terminal_matcher):
def __init__(self, pat):
terminal_matcher.__init__(self, pat)
self.regex = pat
@ -250,6 +276,7 @@ class regex_rule(terminal_matcher):
def pattern(self):
return self.regex
class text_match(terminal_matcher):
alpha_re = re.compile(r'[a-zA-Z]')
@ -274,7 +301,9 @@ class text_match(terminal_matcher):
return '[%s%s]' % (c.upper(), c.lower())
return self.alpha_re.sub(ignorecaseify, re.escape(self.arg))
class case_match(text_match):
def match(self, ctxt, completions):
if ctxt.remainder:
if self.arg == ctxt.remainder[0][1]:
@ -286,15 +315,21 @@ class case_match(text_match):
def pattern(self):
return re.escape(self.arg)
class word_match(text_match):
def pattern(self):
return r'\b' + text_match.pattern(self) + r'\b'
class case_word_match(case_match):
def pattern(self):
return r'\b' + case_match.pattern(self) + r'\b'
class terminal_type_matcher(matcher):
def __init__(self, tokentype, submatcher):
matcher.__init__(self, tokentype)
self.tokentype = tokentype
@ -311,16 +346,17 @@ class terminal_type_matcher(matcher):
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.tokentype, self.submatcher)
class ParsingRuleSet:
RuleSpecScanner = SaferScanner([
(r'::=', lambda s,t: t),
(r'\[[a-z0-9_]+\]=', lambda s,t: ('named_collector', t[1:-2])),
(r'[a-z0-9_]+=', lambda s,t: ('named_symbol', t[:-1])),
(r'/(\[\^?.[^]]*\]|[^/]|\\.)*/', lambda s,t: ('regex', t[1:-1].replace(r'\/', '/'))),
(r'"([^"]|\\.)*"', lambda s,t: ('litstring', t)),
(r'<[^>]*>', lambda s,t: ('reference', t[1:-1])),
(r'\bJUNK\b', lambda s,t: ('junk', t)),
(r'[@()|?*;]', lambda s,t: t),
(r'::=', lambda s, t: t),
(r'\[[a-z0-9_]+\]=', lambda s, t: ('named_collector', t[1:-2])),
(r'[a-z0-9_]+=', lambda s, t: ('named_symbol', t[:-1])),
(r'/(\[\^?.[^]]*\]|[^/]|\\.)*/', lambda s, t: ('regex', t[1:-1].replace(r'\/', '/'))),
(r'"([^"]|\\.)*"', lambda s, t: ('litstring', t)),
(r'<[^>]*>', lambda s, t: ('reference', t[1:-1])),
(r'\bJUNK\b', lambda s, t: ('junk', t)),
(r'[@()|?*;]', lambda s, t: t),
(r'\s+', None),
(r'#[^\n]*', None),
], re.I | re.S)
@ -482,7 +518,8 @@ class ParsingRuleSet:
pattern.match(ctxt, completions)
return completions
import sys, traceback
import sys
class Debugotron(set):
depth = 10

View File

@ -21,7 +21,9 @@
import re
from sre_constants import BRANCH, SUBPATTERN, GROUPREF, GROUPREF_IGNORE, GROUPREF_EXISTS
class SaferScanner(re.Scanner):
def __init__(self, lexicon, flags=0):
self.lexicon = lexicon
p = []
@ -29,9 +31,9 @@ class SaferScanner(re.Scanner):
s.flags = flags
for phrase, action in lexicon:
p.append(re.sre_parse.SubPattern(s, [
(SUBPATTERN, (len(p)+1, self.subpat(phrase, flags))),
]))
s.groups = len(p)+1
(SUBPATTERN, (len(p) + 1, self.subpat(phrase, flags))),
]))
s.groups = len(p) + 1
p = re.sre_parse.SubPattern(s, [(BRANCH, (None, p))])
self.p = p
self.scanner = re.sre_compile.compile(p)

View File

@ -73,7 +73,7 @@ def ssl_settings(host, config_file, env=os.environ):
"to be specified. Please provide path to the certfile in [ssl] section "
"as 'certfile' option in %s (or use [certfiles] section) or set SSL_CERTFILE "
"environment variable." % (config_file,))
if not ssl_certfile is None:
if ssl_certfile is not None:
ssl_certfile = os.path.expanduser(ssl_certfile)
userkey = get_option('ssl', 'userkey')

View File

@ -20,6 +20,7 @@ from datetime import timedelta, tzinfo
ZERO = timedelta(0)
class UTC(tzinfo):
"""UTC"""

View File

@ -77,20 +77,22 @@
# Latest C version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
# auxiliary function for binary search in interval table
def bisearch(ucs, table):
min = 0
max = len(table) - 1
if ucs < table[0][0] or ucs > table[max][1]:
min = 0
max = len(table) - 1
if ucs < table[0][0] or ucs > table[max][1]:
return 0
while max >= min:
mid = (min + max) / 2
if ucs > table[mid][1]:
min = mid + 1
elif ucs < table[mid][0]:
max = mid - 1
else:
return 1
return 0
while max >= min:
mid = (min + max) / 2
if ucs > table[mid][1]:
min = mid + 1
elif ucs < table[mid][0]:
max = mid - 1
else:
return 1
return 0
# The following two functions define the column width of an ISO 10646
# character as follows:
@ -126,152 +128,153 @@ def bisearch(ucs, table):
# sorted list of non-overlapping intervals of non-spacing characters
# generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c"
combining = (
( 0x0300, 0x036F ), ( 0x0483, 0x0486 ), ( 0x0488, 0x0489 ),
( 0x0591, 0x05BD ), ( 0x05BF, 0x05BF ), ( 0x05C1, 0x05C2 ),
( 0x05C4, 0x05C5 ), ( 0x05C7, 0x05C7 ), ( 0x0600, 0x0603 ),
( 0x0610, 0x0615 ), ( 0x064B, 0x065E ), ( 0x0670, 0x0670 ),
( 0x06D6, 0x06E4 ), ( 0x06E7, 0x06E8 ), ( 0x06EA, 0x06ED ),
( 0x070F, 0x070F ), ( 0x0711, 0x0711 ), ( 0x0730, 0x074A ),
( 0x07A6, 0x07B0 ), ( 0x07EB, 0x07F3 ), ( 0x0901, 0x0902 ),
( 0x093C, 0x093C ), ( 0x0941, 0x0948 ), ( 0x094D, 0x094D ),
( 0x0951, 0x0954 ), ( 0x0962, 0x0963 ), ( 0x0981, 0x0981 ),
( 0x09BC, 0x09BC ), ( 0x09C1, 0x09C4 ), ( 0x09CD, 0x09CD ),
( 0x09E2, 0x09E3 ), ( 0x0A01, 0x0A02 ), ( 0x0A3C, 0x0A3C ),
( 0x0A41, 0x0A42 ), ( 0x0A47, 0x0A48 ), ( 0x0A4B, 0x0A4D ),
( 0x0A70, 0x0A71 ), ( 0x0A81, 0x0A82 ), ( 0x0ABC, 0x0ABC ),
( 0x0AC1, 0x0AC5 ), ( 0x0AC7, 0x0AC8 ), ( 0x0ACD, 0x0ACD ),
( 0x0AE2, 0x0AE3 ), ( 0x0B01, 0x0B01 ), ( 0x0B3C, 0x0B3C ),
( 0x0B3F, 0x0B3F ), ( 0x0B41, 0x0B43 ), ( 0x0B4D, 0x0B4D ),
( 0x0B56, 0x0B56 ), ( 0x0B82, 0x0B82 ), ( 0x0BC0, 0x0BC0 ),
( 0x0BCD, 0x0BCD ), ( 0x0C3E, 0x0C40 ), ( 0x0C46, 0x0C48 ),
( 0x0C4A, 0x0C4D ), ( 0x0C55, 0x0C56 ), ( 0x0CBC, 0x0CBC ),
( 0x0CBF, 0x0CBF ), ( 0x0CC6, 0x0CC6 ), ( 0x0CCC, 0x0CCD ),
( 0x0CE2, 0x0CE3 ), ( 0x0D41, 0x0D43 ), ( 0x0D4D, 0x0D4D ),
( 0x0DCA, 0x0DCA ), ( 0x0DD2, 0x0DD4 ), ( 0x0DD6, 0x0DD6 ),
( 0x0E31, 0x0E31 ), ( 0x0E34, 0x0E3A ), ( 0x0E47, 0x0E4E ),
( 0x0EB1, 0x0EB1 ), ( 0x0EB4, 0x0EB9 ), ( 0x0EBB, 0x0EBC ),
( 0x0EC8, 0x0ECD ), ( 0x0F18, 0x0F19 ), ( 0x0F35, 0x0F35 ),
( 0x0F37, 0x0F37 ), ( 0x0F39, 0x0F39 ), ( 0x0F71, 0x0F7E ),
( 0x0F80, 0x0F84 ), ( 0x0F86, 0x0F87 ), ( 0x0F90, 0x0F97 ),
( 0x0F99, 0x0FBC ), ( 0x0FC6, 0x0FC6 ), ( 0x102D, 0x1030 ),
( 0x1032, 0x1032 ), ( 0x1036, 0x1037 ), ( 0x1039, 0x1039 ),
( 0x1058, 0x1059 ), ( 0x1160, 0x11FF ), ( 0x135F, 0x135F ),
( 0x1712, 0x1714 ), ( 0x1732, 0x1734 ), ( 0x1752, 0x1753 ),
( 0x1772, 0x1773 ), ( 0x17B4, 0x17B5 ), ( 0x17B7, 0x17BD ),
( 0x17C6, 0x17C6 ), ( 0x17C9, 0x17D3 ), ( 0x17DD, 0x17DD ),
( 0x180B, 0x180D ), ( 0x18A9, 0x18A9 ), ( 0x1920, 0x1922 ),
( 0x1927, 0x1928 ), ( 0x1932, 0x1932 ), ( 0x1939, 0x193B ),
( 0x1A17, 0x1A18 ), ( 0x1B00, 0x1B03 ), ( 0x1B34, 0x1B34 ),
( 0x1B36, 0x1B3A ), ( 0x1B3C, 0x1B3C ), ( 0x1B42, 0x1B42 ),
( 0x1B6B, 0x1B73 ), ( 0x1DC0, 0x1DCA ), ( 0x1DFE, 0x1DFF ),
( 0x200B, 0x200F ), ( 0x202A, 0x202E ), ( 0x2060, 0x2063 ),
( 0x206A, 0x206F ), ( 0x20D0, 0x20EF ), ( 0x302A, 0x302F ),
( 0x3099, 0x309A ), ( 0xA806, 0xA806 ), ( 0xA80B, 0xA80B ),
( 0xA825, 0xA826 ), ( 0xFB1E, 0xFB1E ), ( 0xFE00, 0xFE0F ),
( 0xFE20, 0xFE23 ), ( 0xFEFF, 0xFEFF ), ( 0xFFF9, 0xFFFB ),
( 0x10A01, 0x10A03 ), ( 0x10A05, 0x10A06 ), ( 0x10A0C, 0x10A0F ),
( 0x10A38, 0x10A3A ), ( 0x10A3F, 0x10A3F ), ( 0x1D167, 0x1D169 ),
( 0x1D173, 0x1D182 ), ( 0x1D185, 0x1D18B ), ( 0x1D1AA, 0x1D1AD ),
( 0x1D242, 0x1D244 ), ( 0xE0001, 0xE0001 ), ( 0xE0020, 0xE007F ),
( 0xE0100, 0xE01EF )
(0x0300, 0x036F), (0x0483, 0x0486), (0x0488, 0x0489),
(0x0591, 0x05BD), (0x05BF, 0x05BF), (0x05C1, 0x05C2),
(0x05C4, 0x05C5), (0x05C7, 0x05C7), (0x0600, 0x0603),
(0x0610, 0x0615), (0x064B, 0x065E), (0x0670, 0x0670),
(0x06D6, 0x06E4), (0x06E7, 0x06E8), (0x06EA, 0x06ED),
(0x070F, 0x070F), (0x0711, 0x0711), (0x0730, 0x074A),
(0x07A6, 0x07B0), (0x07EB, 0x07F3), (0x0901, 0x0902),
(0x093C, 0x093C), (0x0941, 0x0948), (0x094D, 0x094D),
(0x0951, 0x0954), (0x0962, 0x0963), (0x0981, 0x0981),
(0x09BC, 0x09BC), (0x09C1, 0x09C4), (0x09CD, 0x09CD),
(0x09E2, 0x09E3), (0x0A01, 0x0A02), (0x0A3C, 0x0A3C),
(0x0A41, 0x0A42), (0x0A47, 0x0A48), (0x0A4B, 0x0A4D),
(0x0A70, 0x0A71), (0x0A81, 0x0A82), (0x0ABC, 0x0ABC),
(0x0AC1, 0x0AC5), (0x0AC7, 0x0AC8), (0x0ACD, 0x0ACD),
(0x0AE2, 0x0AE3), (0x0B01, 0x0B01), (0x0B3C, 0x0B3C),
(0x0B3F, 0x0B3F), (0x0B41, 0x0B43), (0x0B4D, 0x0B4D),
(0x0B56, 0x0B56), (0x0B82, 0x0B82), (0x0BC0, 0x0BC0),
(0x0BCD, 0x0BCD), (0x0C3E, 0x0C40), (0x0C46, 0x0C48),
(0x0C4A, 0x0C4D), (0x0C55, 0x0C56), (0x0CBC, 0x0CBC),
(0x0CBF, 0x0CBF), (0x0CC6, 0x0CC6), (0x0CCC, 0x0CCD),
(0x0CE2, 0x0CE3), (0x0D41, 0x0D43), (0x0D4D, 0x0D4D),
(0x0DCA, 0x0DCA), (0x0DD2, 0x0DD4), (0x0DD6, 0x0DD6),
(0x0E31, 0x0E31), (0x0E34, 0x0E3A), (0x0E47, 0x0E4E),
(0x0EB1, 0x0EB1), (0x0EB4, 0x0EB9), (0x0EBB, 0x0EBC),
(0x0EC8, 0x0ECD), (0x0F18, 0x0F19), (0x0F35, 0x0F35),
(0x0F37, 0x0F37), (0x0F39, 0x0F39), (0x0F71, 0x0F7E),
(0x0F80, 0x0F84), (0x0F86, 0x0F87), (0x0F90, 0x0F97),
(0x0F99, 0x0FBC), (0x0FC6, 0x0FC6), (0x102D, 0x1030),
(0x1032, 0x1032), (0x1036, 0x1037), (0x1039, 0x1039),
(0x1058, 0x1059), (0x1160, 0x11FF), (0x135F, 0x135F),
(0x1712, 0x1714), (0x1732, 0x1734), (0x1752, 0x1753),
(0x1772, 0x1773), (0x17B4, 0x17B5), (0x17B7, 0x17BD),
(0x17C6, 0x17C6), (0x17C9, 0x17D3), (0x17DD, 0x17DD),
(0x180B, 0x180D), (0x18A9, 0x18A9), (0x1920, 0x1922),
(0x1927, 0x1928), (0x1932, 0x1932), (0x1939, 0x193B),
(0x1A17, 0x1A18), (0x1B00, 0x1B03), (0x1B34, 0x1B34),
(0x1B36, 0x1B3A), (0x1B3C, 0x1B3C), (0x1B42, 0x1B42),
(0x1B6B, 0x1B73), (0x1DC0, 0x1DCA), (0x1DFE, 0x1DFF),
(0x200B, 0x200F), (0x202A, 0x202E), (0x2060, 0x2063),
(0x206A, 0x206F), (0x20D0, 0x20EF), (0x302A, 0x302F),
(0x3099, 0x309A), (0xA806, 0xA806), (0xA80B, 0xA80B),
(0xA825, 0xA826), (0xFB1E, 0xFB1E), (0xFE00, 0xFE0F),
(0xFE20, 0xFE23), (0xFEFF, 0xFEFF), (0xFFF9, 0xFFFB),
(0x10A01, 0x10A03), (0x10A05, 0x10A06), (0x10A0C, 0x10A0F),
(0x10A38, 0x10A3A), (0x10A3F, 0x10A3F), (0x1D167, 0x1D169),
(0x1D173, 0x1D182), (0x1D185, 0x1D18B), (0x1D1AA, 0x1D1AD),
(0x1D242, 0x1D244), (0xE0001, 0xE0001), (0xE0020, 0xE007F),
(0xE0100, 0xE01EF)
)
# sorted list of non-overlapping intervals of East Asian Ambiguous
# characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c"
ambiguous = (
( 0x00A1, 0x00A1 ), ( 0x00A4, 0x00A4 ), ( 0x00A7, 0x00A8 ),
( 0x00AA, 0x00AA ), ( 0x00AE, 0x00AE ), ( 0x00B0, 0x00B4 ),
( 0x00B6, 0x00BA ), ( 0x00BC, 0x00BF ), ( 0x00C6, 0x00C6 ),
( 0x00D0, 0x00D0 ), ( 0x00D7, 0x00D8 ), ( 0x00DE, 0x00E1 ),
( 0x00E6, 0x00E6 ), ( 0x00E8, 0x00EA ), ( 0x00EC, 0x00ED ),
( 0x00F0, 0x00F0 ), ( 0x00F2, 0x00F3 ), ( 0x00F7, 0x00FA ),
( 0x00FC, 0x00FC ), ( 0x00FE, 0x00FE ), ( 0x0101, 0x0101 ),
( 0x0111, 0x0111 ), ( 0x0113, 0x0113 ), ( 0x011B, 0x011B ),
( 0x0126, 0x0127 ), ( 0x012B, 0x012B ), ( 0x0131, 0x0133 ),
( 0x0138, 0x0138 ), ( 0x013F, 0x0142 ), ( 0x0144, 0x0144 ),
( 0x0148, 0x014B ), ( 0x014D, 0x014D ), ( 0x0152, 0x0153 ),
( 0x0166, 0x0167 ), ( 0x016B, 0x016B ), ( 0x01CE, 0x01CE ),
( 0x01D0, 0x01D0 ), ( 0x01D2, 0x01D2 ), ( 0x01D4, 0x01D4 ),
( 0x01D6, 0x01D6 ), ( 0x01D8, 0x01D8 ), ( 0x01DA, 0x01DA ),
( 0x01DC, 0x01DC ), ( 0x0251, 0x0251 ), ( 0x0261, 0x0261 ),
( 0x02C4, 0x02C4 ), ( 0x02C7, 0x02C7 ), ( 0x02C9, 0x02CB ),
( 0x02CD, 0x02CD ), ( 0x02D0, 0x02D0 ), ( 0x02D8, 0x02DB ),
( 0x02DD, 0x02DD ), ( 0x02DF, 0x02DF ), ( 0x0391, 0x03A1 ),
( 0x03A3, 0x03A9 ), ( 0x03B1, 0x03C1 ), ( 0x03C3, 0x03C9 ),
( 0x0401, 0x0401 ), ( 0x0410, 0x044F ), ( 0x0451, 0x0451 ),
( 0x2010, 0x2010 ), ( 0x2013, 0x2016 ), ( 0x2018, 0x2019 ),
( 0x201C, 0x201D ), ( 0x2020, 0x2022 ), ( 0x2024, 0x2027 ),
( 0x2030, 0x2030 ), ( 0x2032, 0x2033 ), ( 0x2035, 0x2035 ),
( 0x203B, 0x203B ), ( 0x203E, 0x203E ), ( 0x2074, 0x2074 ),
( 0x207F, 0x207F ), ( 0x2081, 0x2084 ), ( 0x20AC, 0x20AC ),
( 0x2103, 0x2103 ), ( 0x2105, 0x2105 ), ( 0x2109, 0x2109 ),
( 0x2113, 0x2113 ), ( 0x2116, 0x2116 ), ( 0x2121, 0x2122 ),
( 0x2126, 0x2126 ), ( 0x212B, 0x212B ), ( 0x2153, 0x2154 ),
( 0x215B, 0x215E ), ( 0x2160, 0x216B ), ( 0x2170, 0x2179 ),
( 0x2190, 0x2199 ), ( 0x21B8, 0x21B9 ), ( 0x21D2, 0x21D2 ),
( 0x21D4, 0x21D4 ), ( 0x21E7, 0x21E7 ), ( 0x2200, 0x2200 ),
( 0x2202, 0x2203 ), ( 0x2207, 0x2208 ), ( 0x220B, 0x220B ),
( 0x220F, 0x220F ), ( 0x2211, 0x2211 ), ( 0x2215, 0x2215 ),
( 0x221A, 0x221A ), ( 0x221D, 0x2220 ), ( 0x2223, 0x2223 ),
( 0x2225, 0x2225 ), ( 0x2227, 0x222C ), ( 0x222E, 0x222E ),
( 0x2234, 0x2237 ), ( 0x223C, 0x223D ), ( 0x2248, 0x2248 ),
( 0x224C, 0x224C ), ( 0x2252, 0x2252 ), ( 0x2260, 0x2261 ),
( 0x2264, 0x2267 ), ( 0x226A, 0x226B ), ( 0x226E, 0x226F ),
( 0x2282, 0x2283 ), ( 0x2286, 0x2287 ), ( 0x2295, 0x2295 ),
( 0x2299, 0x2299 ), ( 0x22A5, 0x22A5 ), ( 0x22BF, 0x22BF ),
( 0x2312, 0x2312 ), ( 0x2460, 0x24E9 ), ( 0x24EB, 0x254B ),
( 0x2550, 0x2573 ), ( 0x2580, 0x258F ), ( 0x2592, 0x2595 ),
( 0x25A0, 0x25A1 ), ( 0x25A3, 0x25A9 ), ( 0x25B2, 0x25B3 ),
( 0x25B6, 0x25B7 ), ( 0x25BC, 0x25BD ), ( 0x25C0, 0x25C1 ),
( 0x25C6, 0x25C8 ), ( 0x25CB, 0x25CB ), ( 0x25CE, 0x25D1 ),
( 0x25E2, 0x25E5 ), ( 0x25EF, 0x25EF ), ( 0x2605, 0x2606 ),
( 0x2609, 0x2609 ), ( 0x260E, 0x260F ), ( 0x2614, 0x2615 ),
( 0x261C, 0x261C ), ( 0x261E, 0x261E ), ( 0x2640, 0x2640 ),
( 0x2642, 0x2642 ), ( 0x2660, 0x2661 ), ( 0x2663, 0x2665 ),
( 0x2667, 0x266A ), ( 0x266C, 0x266D ), ( 0x266F, 0x266F ),
( 0x273D, 0x273D ), ( 0x2776, 0x277F ), ( 0xE000, 0xF8FF ),
( 0xFFFD, 0xFFFD ), ( 0xF0000, 0xFFFFD ), ( 0x100000, 0x10FFFD )
(0x00A1, 0x00A1), (0x00A4, 0x00A4), (0x00A7, 0x00A8),
(0x00AA, 0x00AA), (0x00AE, 0x00AE), (0x00B0, 0x00B4),
(0x00B6, 0x00BA), (0x00BC, 0x00BF), (0x00C6, 0x00C6),
(0x00D0, 0x00D0), (0x00D7, 0x00D8), (0x00DE, 0x00E1),
(0x00E6, 0x00E6), (0x00E8, 0x00EA), (0x00EC, 0x00ED),
(0x00F0, 0x00F0), (0x00F2, 0x00F3), (0x00F7, 0x00FA),
(0x00FC, 0x00FC), (0x00FE, 0x00FE), (0x0101, 0x0101),
(0x0111, 0x0111), (0x0113, 0x0113), (0x011B, 0x011B),
(0x0126, 0x0127), (0x012B, 0x012B), (0x0131, 0x0133),
(0x0138, 0x0138), (0x013F, 0x0142), (0x0144, 0x0144),
(0x0148, 0x014B), (0x014D, 0x014D), (0x0152, 0x0153),
(0x0166, 0x0167), (0x016B, 0x016B), (0x01CE, 0x01CE),
(0x01D0, 0x01D0), (0x01D2, 0x01D2), (0x01D4, 0x01D4),
(0x01D6, 0x01D6), (0x01D8, 0x01D8), (0x01DA, 0x01DA),
(0x01DC, 0x01DC), (0x0251, 0x0251), (0x0261, 0x0261),
(0x02C4, 0x02C4), (0x02C7, 0x02C7), (0x02C9, 0x02CB),
(0x02CD, 0x02CD), (0x02D0, 0x02D0), (0x02D8, 0x02DB),
(0x02DD, 0x02DD), (0x02DF, 0x02DF), (0x0391, 0x03A1),
(0x03A3, 0x03A9), (0x03B1, 0x03C1), (0x03C3, 0x03C9),
(0x0401, 0x0401), (0x0410, 0x044F), (0x0451, 0x0451),
(0x2010, 0x2010), (0x2013, 0x2016), (0x2018, 0x2019),
(0x201C, 0x201D), (0x2020, 0x2022), (0x2024, 0x2027),
(0x2030, 0x2030), (0x2032, 0x2033), (0x2035, 0x2035),
(0x203B, 0x203B), (0x203E, 0x203E), (0x2074, 0x2074),
(0x207F, 0x207F), (0x2081, 0x2084), (0x20AC, 0x20AC),
(0x2103, 0x2103), (0x2105, 0x2105), (0x2109, 0x2109),
(0x2113, 0x2113), (0x2116, 0x2116), (0x2121, 0x2122),
(0x2126, 0x2126), (0x212B, 0x212B), (0x2153, 0x2154),
(0x215B, 0x215E), (0x2160, 0x216B), (0x2170, 0x2179),
(0x2190, 0x2199), (0x21B8, 0x21B9), (0x21D2, 0x21D2),
(0x21D4, 0x21D4), (0x21E7, 0x21E7), (0x2200, 0x2200),
(0x2202, 0x2203), (0x2207, 0x2208), (0x220B, 0x220B),
(0x220F, 0x220F), (0x2211, 0x2211), (0x2215, 0x2215),
(0x221A, 0x221A), (0x221D, 0x2220), (0x2223, 0x2223),
(0x2225, 0x2225), (0x2227, 0x222C), (0x222E, 0x222E),
(0x2234, 0x2237), (0x223C, 0x223D), (0x2248, 0x2248),
(0x224C, 0x224C), (0x2252, 0x2252), (0x2260, 0x2261),
(0x2264, 0x2267), (0x226A, 0x226B), (0x226E, 0x226F),
(0x2282, 0x2283), (0x2286, 0x2287), (0x2295, 0x2295),
(0x2299, 0x2299), (0x22A5, 0x22A5), (0x22BF, 0x22BF),
(0x2312, 0x2312), (0x2460, 0x24E9), (0x24EB, 0x254B),
(0x2550, 0x2573), (0x2580, 0x258F), (0x2592, 0x2595),
(0x25A0, 0x25A1), (0x25A3, 0x25A9), (0x25B2, 0x25B3),
(0x25B6, 0x25B7), (0x25BC, 0x25BD), (0x25C0, 0x25C1),
(0x25C6, 0x25C8), (0x25CB, 0x25CB), (0x25CE, 0x25D1),
(0x25E2, 0x25E5), (0x25EF, 0x25EF), (0x2605, 0x2606),
(0x2609, 0x2609), (0x260E, 0x260F), (0x2614, 0x2615),
(0x261C, 0x261C), (0x261E, 0x261E), (0x2640, 0x2640),
(0x2642, 0x2642), (0x2660, 0x2661), (0x2663, 0x2665),
(0x2667, 0x266A), (0x266C, 0x266D), (0x266F, 0x266F),
(0x273D, 0x273D), (0x2776, 0x277F), (0xE000, 0xF8FF),
(0xFFFD, 0xFFFD), (0xF0000, 0xFFFFD), (0x100000, 0x10FFFD)
)
def mk_wcwidth(ucs):
# test for 8-bit control characters
if ucs == 0:
return 0
if ucs < 32 or (ucs >= 0x7f and ucs < 0xa0):
return -1
# test for 8-bit control characters
if ucs == 0:
return 0
if ucs < 32 or (ucs >= 0x7f and ucs < 0xa0):
return -1
# binary search in table of non-spacing characters
if bisearch(ucs, combining):
return 0
# binary search in table of non-spacing characters
if bisearch(ucs, combining):
return 0
# if we arrive here, ucs is not a combining or C0/C1 control character
# if we arrive here, ucs is not a combining or C0/C1 control character
return 1 + \
int(ucs >= 0x1100 and
(ucs <= 0x115f or # Hangul Jamo init. consonants
ucs == 0x2329 or ucs == 0x232a or
(ucs >= 0x2e80 and ucs <= 0xa4cf and
ucs != 0x303f) or # CJK ... Yi
(ucs >= 0xac00 and ucs <= 0xd7a3) or # Hangul Syllables
(ucs >= 0xf900 and ucs <= 0xfaff) or # CJK Compatibility Ideographs
(ucs >= 0xfe10 and ucs <= 0xfe19) or # Vertical forms
(ucs >= 0xfe30 and ucs <= 0xfe6f) or # CJK Compatibility Forms
(ucs >= 0xff00 and ucs <= 0xff60) or # Fullwidth Forms
(ucs >= 0xffe0 and ucs <= 0xffe6) or
(ucs >= 0x20000 and ucs <= 0x2fffd) or
(ucs >= 0x30000 and ucs <= 0x3fffd)))
return 1 + \
int(ucs >= 0x1100 and
(ucs <= 0x115f or # Hangul Jamo init. consonants
ucs == 0x2329 or ucs == 0x232a or
(ucs >= 0x2e80 and ucs <= 0xa4cf and
ucs != 0x303f) or # CJK ... Yi
(ucs >= 0xac00 and ucs <= 0xd7a3) or # Hangul Syllables
(ucs >= 0xf900 and ucs <= 0xfaff) or # CJK Compatibility Ideographs
(ucs >= 0xfe10 and ucs <= 0xfe19) or # Vertical forms
(ucs >= 0xfe30 and ucs <= 0xfe6f) or # CJK Compatibility Forms
(ucs >= 0xff00 and ucs <= 0xff60) or # Fullwidth Forms
(ucs >= 0xffe0 and ucs <= 0xffe6) or
(ucs >= 0x20000 and ucs <= 0x2fffd) or
(ucs >= 0x30000 and ucs <= 0x3fffd)))
def mk_wcswidth(pwcs):
width = 0
for c in pwcs:
w = mk_wcwidth(c)
if w < 0:
return -1
else:
width += w
width = 0
for c in pwcs:
w = mk_wcwidth(c)
if w < 0:
return -1
else:
width += w
return width
return width
# The following functions are the same as mk_wcwidth() and
@ -282,34 +285,39 @@ def mk_wcswidth(pwcs):
# the traditional terminal character-width behaviour. It is not
# otherwise recommended for general use.
def mk_wcwidth_cjk(ucs):
# binary search in table of non-spacing characters
if bisearch(ucs, ambiguous):
return 2
# binary search in table of non-spacing characters
if bisearch(ucs, ambiguous):
return 2
return mk_wcwidth(ucs)
return mk_wcwidth(ucs)
def mk_wcswidth_cjk(pwcs):
width = 0
width = 0
for c in pwcs:
w = mk_wcwidth_cjk(c)
if w < 0:
return -1
width += w
for c in pwcs:
w = mk_wcwidth_cjk(c)
if w < 0:
return -1
width += w
return width
return width
# python-y versions, dealing with unicode objects
def wcwidth(c):
return mk_wcwidth(ord(c))
def wcswidth(s):
return mk_wcswidth(map(ord, s))
def wcwidth_cjk(c):
return mk_wcwidth_cjk(ord(c))
def wcswidth_cjk(s):
return mk_wcswidth_cjk(map(ord, s))
@ -338,12 +346,12 @@ if __name__ == "__main__":
uchr = unicodedata.lookup(name)
calculatedwidth = wcwidth(uchr)
assert calculatedwidth == printwidth, \
'width for %r should be %d, but is %d?' % (uchr, printwidth, calculatedwidth)
'width for %r should be %d, but is %d?' % (uchr, printwidth, calculatedwidth)
for c in nonprinting:
calculatedwidth = wcwidth(c)
assert calculatedwidth < 0, \
'%r is a control character, but wcwidth gives %d' % (c, calculatedwidth)
'%r is a control character, but wcwidth gives %d' % (c, calculatedwidth)
assert wcwidth('\0') == 0 # special case