def parse_array(self, token): # Parsing array rank and dimensions rank = parse_varint(self.reader) if rank == 0: raise WolframParserException("Array rank cannot be zero.") token.dimensions = [] for i in range(rank): dim = parse_varint(self.reader) if dim == 0: raise WolframParserException("Array dimensions cannot be zero.") token.dimensions.append(dim) # reading values bytecount = constants.ARRAY_TYPES_ELEM_SIZE[token.array_type] * token.element_count token.data = self.reader.read(bytecount)
def parse_header(self): compress = False next_byte = self.reader.read(1) if next_byte == WXF_VERSION: version = int(next_byte) next_byte = self.reader.read(1) else: raise WolframParserException("Invalid version %s." % next_byte) if next_byte == WXF_HEADER_COMPRESS: compress = True next_byte = self.reader.read(1) if next_byte != WXF_HEADER_SEPARATOR: raise WolframParserException( "Invalid header. Failed to find header separator ':'.") return (version, compress)
def consume_bigint(self, current_token, tokens, **kwargs): """Consume a :class:`~wolframclient.deserializers.wxf.wxfparser.WXFToken` of type *big integer* as a :class:`int`.""" try: return int(current_token.data) except ValueError: raise WolframParserException("Invalid big integer value: %s" % current_token.data)
def parse_varint(reader): """Parse a readable binary buffer for a positive varint encoded integer.""" count = 0 continuation = True shift = 0 length = 0 # when we read from stream we get a sequence of bytes. Its length is 1 # except if we reached EOF in which case taking index 0 raises IndexError. try: while continuation and count < 8: count += 1 next_byte = reader.read(1) next_byte = ord(next_byte) length |= (next_byte & 0x7F) << shift shift = shift + 7 continuation = (next_byte & 0x80) != 0 if continuation: next_byte = reader.read(1) next_byte = ord(next_byte) next_byte &= 0x7F if next_byte == 0: raise WolframParserException("Invalid last varint byte.") length |= next_byte << shift return length except IndexError: raise EOFError("EOF reached while parsing varint encoded integer.")
def token_for_rule(self, token): if not self.context.is_rule_valid(): raise WolframParserException( "Rule and RuleDelayed must be parts of an Association." ) self.context.step_into_new_rule() return token
def _consumer_from_type(self, wxf_type): try: func = self._mapping[wxf_type] except KeyError: raise WolframParserException( "Class %s does not implement any consumer method for WXF token %s" % (self.__class__.__name__, wxf_type)) return getattr(self, func)
def token_for_numeric_array(self, token): self.context.add_part() token.array_type = self.reader.read(1) if token.array_type not in constants.ARRAY_TYPES_ELEM_SIZE: raise WolframParserException( "Invalid NumericArray value type: %s" % token.array_type) self.parse_array(token) return token
def token_for_packed_array(self, token): self.context.add_part() token.array_type = self.reader.read(1) if token.array_type not in constants.VALID_PACKED_ARRAY_TYPES: raise WolframParserException("Invalid PackedArray value type: %s" % token.array_type) self.parse_array(token) return token
def next_token(self): next_byte = self.reader.read(1) try: handler = self._mapping[next_byte] except KeyError: raise WolframParserException("Unexpected token %s" % next_byte) return getattr(self, handler)(WXFToken(next_byte))
def binary_deserialize(wxf_input, consumer=None, **kwargs): """Deserialize binary data and return a Python object. Serialize a Python object to WXF:: >>> wxf = export({'key' : [1,2,3]}, target_format='wxf') Retrieve the input object:: >>> binary_deserialize(wxf) {'key': [1, 2, 3]} A stream of :class:`~wolframclient.deserializers.wxf.wxfparser.WXFToken` is generated from the WXF input by a instance of :class:`~wolframclient.deserializers.wxf.wxfparser.WXFParser`. The consumer must be an instance of :class:`~wolframclient.deserializers.wxf.wxfconsumer.WXFConsumer`. If none is provided, :class:`~wolframclient.deserializers.wxf.wxfconsumer.WXFConsumer` is used. To enable NumPy array support, use :class:`~wolframclient.deserializers.wxf.wxfconsumer.WXFConsumerNumpy`. Named parameters are passed to the consumer. They can be any valid parameter of :meth:`~wolframclient.deserializers.wxf.wxfconsumer.WXFConsumer.next_expression`, namely: * `dict_class`: map WXF `Association` to `dict_class` in place of a regular :class:`dict` """ parser = WXFParser(wxf_input) if consumer is None: consumer = WXFConsumer() try: o = consumer.next_expression(parser.tokens(), **kwargs) except StopIteration: raise WolframParserException( 'Input data does not represent a valid expression in WXF format. Expecting more input data.' ) if not parser.context.is_valid_final_state(): raise WolframParserException( 'Input data does not represent a valid expression in WXF format. Some expressions are incomplete.' ) return o
def consume_bigreal(self, current_token, tokens, **kwargs): """Parse a WXF big real as a WXF serializable big real. There is not such thing as a big real, in Wolfram Language notation, in Python. This wrapper ensures round tripping of big reals without the need of `ToExpression`. Introducing `ToExpression` would imply to marshall the big real data to avoid malicious code from being introduced in place of an actual real. """ match = self.BIGREAL_RE.match(current_token.data) if match: num, _, _, exp = match.groups() if exp: return decimal.Decimal("%se%s" % (num, exp)) return decimal.Decimal(num) raise WolframParserException("Invalid big real value: %s" % current_token.data)