diff --git a/cassandra/decoder.py b/cassandra/decoder.py index 199cab6f..8b8c9191 100644 --- a/cassandra/decoder.py +++ b/cassandra/decoder.py @@ -22,7 +22,7 @@ except ImportError: from cassandra.marshal import (int32_pack, int32_unpack, uint16_pack, uint16_unpack, int8_pack, int8_unpack) -from cassandra.cqltypes import lookup_cqltype +from cassandra.types import lookup_cqltype class NotSupportedError(Exception): diff --git a/cassandra/metadata.py b/cassandra/metadata.py index e929db98..3a4ab305 100644 --- a/cassandra/metadata.py +++ b/cassandra/metadata.py @@ -12,7 +12,7 @@ try: except ImportError: pass -import cassandra.cqltypes as cqltypes +import cassandra.types as types from cassandra.marshal import varint_unpack from cassandra.pool import Host @@ -102,8 +102,8 @@ class Metadata(object): def _build_table_metadata(self, keyspace_metadata, row, col_rows): cfname = row["columnfamily_name"] - comparator = cqltypes.lookup_casstype(row["comparator"]) - if issubclass(comparator, cqltypes.CompositeType): + comparator = types.lookup_casstype(row["comparator"]) + if issubclass(comparator, types.CompositeType): column_name_types = comparator.subtypes is_composite = True else: @@ -115,13 +115,13 @@ class Metadata(object): column_aliases = json.loads(row["column_aliases"]) if is_composite: - if issubclass(last_col, cqltypes.ColumnToCollectionType): + if issubclass(last_col, types.ColumnToCollectionType): # collections is_compact = False has_value = False clustering_size = num_column_name_components - 2 elif (len(column_aliases) == num_column_name_components - 1 - and issubclass(last_col, cqltypes.UTF8Type)): + and issubclass(last_col, types.UTF8Type)): # aliases? is_compact = False has_value = False @@ -147,8 +147,8 @@ class Metadata(object): key_aliases = row.get("key_aliases") key_aliases = json.loads(key_aliases) if key_aliases else [] - key_type = cqltypes.lookup_casstype(row["key_validator"]) - key_types = key_type.subtypes if issubclass(key_type, cqltypes.CompositeType) else [key_type] + key_type = types.lookup_casstype(row["key_validator"]) + key_types = key_type.subtypes if issubclass(key_type, types.CompositeType) else [key_type] for i, col_type in enumerate(key_types): if len(key_aliases) > i: column_name = key_aliases[i] @@ -174,7 +174,7 @@ class Metadata(object): # value alias (if present) if has_value: - validator = cqltypes.lookup_casstype(row["default_validator"]) + validator = types.lookup_casstype(row["default_validator"]) if not key_aliases: # TODO are we checking the right thing here? value_alias = "value" else: @@ -200,7 +200,7 @@ class Metadata(object): def _build_column_metadata(self, table_metadata, row): name = row["column_name"] - data_type = cqltypes.lookup_casstype(row["validator"]) + data_type = types.lookup_casstype(row["validator"]) column_meta = ColumnMetadata(table_metadata, name, data_type) index_meta = self._build_index_metadata(column_meta, row) column_meta.index = index_meta @@ -359,14 +359,14 @@ class TableMetadata(object): clustering_names = self.protect_names([c.name for c in self.clustering_key]) if self.options.get("is_compact_storage") and \ - not issubclass(self.comparator, cqltypes.CompositeType): + not issubclass(self.comparator, types.CompositeType): subtypes = [self.comparator] else: subtypes = self.comparator.subtypes inner = [] for colname, coltype in zip(clustering_names, subtypes): - ordering = "DESC" if issubclass(coltype, cqltypes.ReversedType) else "ASC" + ordering = "DESC" if issubclass(coltype, types.ReversedType) else "ASC" inner.append("%s %s" % (colname, ordering)) cluster_str += "(%s)" % ", ".join(inner) @@ -434,7 +434,7 @@ class ColumnMetadata(object): @property def typestring(self): - if issubclass(self.data_type, cqltypes.ReversedType): + if issubclass(self.data_type, types.ReversedType): return self.data_type.subtypes[0].cql_parameterized_type() else: return self.data_type.cql_parameterized_type() diff --git a/cassandra/cqltypes.py b/cassandra/types.py similarity index 91% rename from cassandra/cqltypes.py rename to cassandra/types.py index 0428081e..b5d3bc84 100644 --- a/cassandra/cqltypes.py +++ b/cassandra/types.py @@ -1,19 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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. - """ Representation of Cassandra data types. These classes should make it simple for the library (and caller software) to deal with Cassandra-style Java class type @@ -21,13 +5,14 @@ names and CQL type specifiers, and convert between them cleanly. Parameterized types are fully supported in both flavors. Once you have the right Type object for the type you want, you can use it to serialize, deserialize, or retrieve the corresponding CQL or Cassandra type strings. - -If/when the need arises for interpret types from CQL string literals in -different ways (for https://issues.apache.org/jira/browse/CASSANDRA-3799, -for example), these classes would be a good place to tack on -.from_cql_literal() and .as_cql_literal() classmethods (or whatever). """ +# NOTE: +# If/when the need arises for interpret types from CQL string literals in +# different ways (for https://issues.apache.org/jira/browse/CASSANDRA-3799, +# for example), these classes would be a good place to tack on +# .from_cql_literal() and .as_cql_literal() classmethods (or whatever). + import calendar from decimal import Decimal import re @@ -38,7 +23,7 @@ from uuid import UUID try: from cStringIO import StringIO except ImportError: - from StringIO import StringIO + from StringIO import StringIO # NOQA from cassandra.marshal import (int8_pack, int8_unpack, uint16_pack, uint16_unpack, int32_pack, int32_unpack, int64_pack, int64_unpack, @@ -77,12 +62,14 @@ class CassandraTypeType(type): _cqltypes[cls.typename] = cls return cls + casstype_scanner = re.Scanner(( (r'[()]', lambda s, t: t), (r'[a-zA-Z0-9_.:]+', lambda s, t: t), (r'[\s,]', None), )) + def lookup_casstype_simple(casstype): """ Given a Cassandra type name (either fully distinguished or not), hand @@ -93,7 +80,6 @@ def lookup_casstype_simple(casstype): nothing with parentheses). Use lookup_casstype() instead if you might need that. """ - shortname = trim_if_startswith(casstype, apache_cassandra_type_prefix) try: typeclass = _casstypes[shortname] @@ -101,6 +87,7 @@ def lookup_casstype_simple(casstype): typeclass = mkUnrecognizedType(casstype) return typeclass + def parse_casstype_args(typestring): tokens, remainder = casstype_scanner.scan(typestring) if remainder: @@ -121,10 +108,10 @@ def parse_casstype_args(typestring): tok = tok.rsplit(':', 1)[-1] ctype = lookup_casstype_simple(tok) args[-1].append(ctype) - assert len(args) == 1, args - assert len(args[0]) == 1, args[0] + return args[0][0] + def lookup_casstype(casstype): """ Given a Cassandra type as a string (possibly including parameters), hand @@ -134,10 +121,9 @@ def lookup_casstype(casstype): Example: >>> lookup_casstype('org.apache.cassandra.db.marshal.MapType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.Int32Type)') - + """ - if isinstance(casstype, (CassandraType, CassandraTypeType)): return casstype try: @@ -145,6 +131,7 @@ def lookup_casstype(casstype): except (ValueError, AssertionError, IndexError), e: raise ValueError("Don't know how to parse type string %r: %s" % (casstype, e)) + def lookup_cqltype(cqltype): """ Given a CQL type specifier, possibly including parameters, hand back the @@ -156,10 +143,9 @@ def lookup_cqltype(cqltype): Example: >>> lookup_cqltype('map') - + """ - if isinstance(cqltype, CassandraType): return cqltype args = () @@ -176,6 +162,7 @@ def lookup_cqltype(cqltype): typeclass = typeclass.apply_parameters(*args) return typeclass + class _CassandraType(object): __metaclass__ = CassandraTypeType subtypes = () @@ -196,7 +183,6 @@ class _CassandraType(object): for this class. As an example, the BooleanType class uses this to convert an incoming value to True or False. """ - return val @classmethod @@ -206,10 +192,7 @@ class _CassandraType(object): for more information. This method differs in that if None or the empty string is passed in, None may be returned. """ - - if byts is None: - return None - if byts == '' and not cls.empty_binary_ok: + if byts is None or (byts == '' and not cls.empty_binary_ok): return None return cls.deserialize(byts) @@ -220,10 +203,7 @@ class _CassandraType(object): more information. This method differs in that if None is passed in, the result is the empty string. """ - - if val is None: - return '' - return cls.serialize(val) + return '' if val is None else cls.serialize(val) @staticmethod def deserialize(byts): @@ -261,7 +241,6 @@ class _CassandraType(object): >>> SetType.cass_parameterized_type_with([DecimalType], full=True) 'org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.DecimalType)' """ - cname = cls.cassname if full and '.' not in cname: cname = apache_cassandra_type_prefix + cname @@ -277,9 +256,8 @@ class _CassandraType(object): using them as parameters. This is how composite types are constructed. >>> MapType.apply_parameters(DateType, BooleanType) - + """ - if cls.num_subtypes != 'UNKNOWN' and len(subtypes) != cls.num_subtypes: raise ValueError("%s types require %d subtypes (%d given)" % (cls.typename, cls.num_subtypes, len(subtypes))) @@ -292,7 +270,6 @@ class _CassandraType(object): Return a CQL type specifier for this type. If this type has parameters, they are included in standard CQL <> notation. """ - if not cls.subtypes: return cls.typename return '%s<%s>' % (cls.typename, ', '.join(styp.cql_parameterized_type() for styp in cls.subtypes)) @@ -305,18 +282,22 @@ class _CassandraType(object): """ return cls.cass_parameterized_type_with(cls.subtypes, full=full) + # it's initially named with a _ to avoid registering it as a real type, but # client programs may want to use the name still for isinstance(), etc CassandraType = _CassandraType + class _UnrecognizedType(_CassandraType): num_subtypes = 'UNKNOWN' + def mkUnrecognizedType(casstypename): return CassandraTypeType(casstypename.encode('utf8'), (_UnrecognizedType,), {'typename': "'%s'" % casstypename}) + class BytesType(_CassandraType): typename = 'blob' empty_binary_ok = True @@ -329,6 +310,7 @@ class BytesType(_CassandraType): def serialize(val): return str(val) + class DecimalType(_CassandraType): typename = 'decimal' @@ -352,6 +334,7 @@ class DecimalType(_CassandraType): unscaled = varint_pack(unscaled) return scale + unscaled + class UUIDType(_CassandraType): typename = 'uuid' @@ -363,6 +346,7 @@ class UUIDType(_CassandraType): def serialize(uuid): return uuid.bytes + class BooleanType(_CassandraType): typename = 'boolean' @@ -378,40 +362,47 @@ class BooleanType(_CassandraType): def serialize(truth): return int8_pack(bool(truth)) + class AsciiType(_CassandraType): typename = 'ascii' empty_binary_ok = True + class FloatType(_CassandraType): typename = 'float' deserialize = staticmethod(float_unpack) serialize = staticmethod(float_pack) + class DoubleType(_CassandraType): typename = 'double' deserialize = staticmethod(double_unpack) serialize = staticmethod(double_pack) + class LongType(_CassandraType): typename = 'bigint' deserialize = staticmethod(int64_unpack) serialize = staticmethod(int64_pack) + class Int32Type(_CassandraType): typename = 'int' deserialize = staticmethod(int32_unpack) serialize = staticmethod(int32_pack) + class IntegerType(_CassandraType): typename = 'varint' deserialize = staticmethod(varint_unpack) serialize = staticmethod(varint_pack) + class InetAddressType(_CassandraType): typename = 'inet' @@ -431,12 +422,14 @@ class InetAddressType(_CassandraType): fam = socket.AF_INET return socket.inet_pton(fam, addr) + class CounterColumnType(_CassandraType): typename = 'counter' deserialize = staticmethod(int64_unpack) serialize = staticmethod(int64_pack) + cql_time_formats = ( '%Y-%m-%d %H:%M', '%Y-%m-%d %H:%M:%S', @@ -445,6 +438,7 @@ cql_time_formats = ( '%Y-%m-%d' ) + class DateType(_CassandraType): typename = 'timestamp' @@ -481,6 +475,7 @@ class DateType(_CassandraType): def serialize(timestamp): return int64_pack(timestamp * 1000) + class TimeUUIDType(DateType): typename = 'timeuuid' @@ -495,6 +490,7 @@ class TimeUUIDType(DateType): def serialize(timeuuid): return timeuuid.bytes + class UTF8Type(_CassandraType): typename = 'text' empty_binary_ok = True @@ -507,6 +503,7 @@ class UTF8Type(_CassandraType): def serialize(ustr): return ustr.encode('utf8') + # name alias _cqltypes['varchar'] = _cqltypes['text'] @@ -530,6 +527,7 @@ class _ParameterizedType(_CassandraType): % cls.typename) return cls.serialize_safe(val) + class _SimpleParameterizedType(_ParameterizedType): @classmethod def validate(cls, val): @@ -561,16 +559,19 @@ class _SimpleParameterizedType(_ParameterizedType): buf.write(itembytes) return buf.getvalue() + class ListType(_SimpleParameterizedType): typename = 'list' num_subtypes = 1 adapter = tuple + class SetType(_SimpleParameterizedType): typename = 'set' num_subtypes = 1 adapter = set + class MapType(_ParameterizedType): typename = 'map' num_subtypes = 2 @@ -614,10 +615,12 @@ class MapType(_ParameterizedType): buf.write(valbytes) return buf.getvalue() + class CompositeType(_ParameterizedType): typename = "'org.apache.cassandra.db.marshal.CompositeType'" num_subtypes = 'UNKNOWN' + class ColumnToCollectionType(_ParameterizedType): """ This class only really exists so that we can cleanly evaluate types when @@ -627,6 +630,7 @@ class ColumnToCollectionType(_ParameterizedType): typename = "'org.apache.cassandra.db.marshal.ColumnToCollectionType'" num_subtypes = 'UNKNOWN' + class ReversedType(_ParameterizedType): typename = "'org.apache.cassandra.db.marshal.ReversedType'" num_subtypes = 1 @@ -641,15 +645,17 @@ class ReversedType(_ParameterizedType): subtype, = cls.subtypes return subtype.to_binary(val) + def is_counter_type(t): if isinstance(t, basestring): t = lookup_casstype(t) return issubclass(t, CounterColumnType) -cql_type_to_apache_class = dict([(c, t.cassname) for (c, t) in _cqltypes.items()]) -apache_class_to_cql_type = dict([(n, t.typename) for (n, t) in _casstypes.items()]) -cql_types = sorted([t for t in _cqltypes.keys() if not t.startswith("'")]) +cql_type_to_apache_class = dict((c, t.cassname) for (c, t) in _cqltypes.items()) +apache_class_to_cql_type = dict((n, t.typename) for (n, t) in _casstypes.items()) + +cql_types = sorted(t for t in _cqltypes.keys() if not t.startswith("'")) def cql_typename(casstypename): """