def test_pretty_class(): class Foobar: def __pretty__(self, fmt, **kwargs): yield 'xxx' assert pformat(Foobar()) == 'xxx' assert pformat(Foobar) == "<class 'tests.test_custom_pretty.test_pretty_class.<locals>.Foobar'>"
def meet_text_prefix( chain: MessageChain, command_name: str, command_config: CommandConfig, ) -> Tuple[bool, str]: aliases: Set[str] = set(command_config.aliases) if command_config.include_class_name: aliases.add(command_name) # aliases.add("") # 保证下方循环至少执行一次 if command_config.need_prefix: prefixes: Iterable[str] = set(command_config.prefixes) else: # 保证下方循环至少执行一次 prefixes = [""] logger.debug(pformat(command_name)) logger.debug(pformat(prefixes)) logger.debug(pformat(aliases)) for alias in aliases: for prefix in prefixes: final_prefix = prefix + alias logger.debug(pformat(final_prefix)) if re.search(f"^{final_prefix}", chain.pure_text): return True, final_prefix return False, ""
def meet_command_exit(chain: MessageChain, command_config: CommandConfig): """退出判断""" logger.debug(pformat(command_config.exit_patterns)) for pattern in command_config.exit_patterns: logger.debug(pformat(re.search(pattern, chain.pure_text))) if re.search(pattern, chain.pure_text): return True return False
def test_devtools_output(): class MyTestModel(BaseModel): a = 1 b = [1, 2, 3] assert devtools.pformat( MyTestModel()) == 'MyTestModel(\n a=1,\n b=[1, 2, 3],\n)'
def test_yield_other(): class CustomCls: def __pretty__(self, fmt, **kwargs): yield fmt('xxx') yield 123 my_cls = CustomCls() v = pformat(my_cls) assert v == "'xxx'123"
def dev(path: StrPath, port: int): config = get_config(path) config.mode = Mode.development logger.debug('Config:\n%s', devtools.pformat(config.dict())) _empty_dir(config.dist_dir) _empty_dir(config.get_tmp_dir()) loop = asyncio.get_event_loop() return loop.run_until_complete(adev(config, port))
async def adjust( self, adjustments: list[Adjustment], control: Control ) -> Description: summary = f"[{', '.join(list(map(str, adjustments)))}]" self.logger.info(f"Adjusting... {summary}") self.logger.trace(devtools.pformat(adjustments)) self.logger.trace(devtools.pformat(control)) aggregate_description = Description.construct() results = await self.servo.dispatch_event( servo.Events.adjust, adjustments=adjustments, control=control ) for result in results: description = result.value aggregate_description.components.extend(description.components) aggregate_description.metrics.extend(description.metrics) self.logger.success(f"Adjustment completed {summary}") return aggregate_description
def test_devtools_compatibility(example_model_factory): model = example_model_factory() model_class = model.__class__ formatted_string = devtools.pformat(model) assert hasattr(model_class, "__pretty__") assert callable(model_class.__pretty__) assert f"{model_class.__name__}(" in formatted_string for name in model_class.__datamodel_fields__.keys(): assert f"{name}=" in formatted_string
async def run_class_handlers( protocol: T_BotProtocol, mode: T_RouteMode, source_id: str, raw_event: Dict, event_name: str, class_handler_names: Set[str], ): kwargs = await get_kwargs(protocol, mode, source_id, event_name, raw_event) logger.debug(pformat(kwargs)) for class_handler_name in class_handler_names: class_handler_cache = class_handler_mapping[class_handler_name] logger.debug(pformat(class_handler_cache)) event_handler = class_handler_cache.event_handlers.get(event_name) # logger.debug(pformat(event_name, event_handler)) if event_handler: logger.info(f"开始执行 {class_handler_name} 的 {event_name} 事件响应") await await_or_sync(event_handler, **fit_kwargs(event_handler, kwargs))
def build(path: StrPath, steps: Set[BuildSteps] = None, mode: Optional[Mode] = None): completed_logger.info('building site...') config = get_config(path) if mode: config.mode = mode logger.debug('Config: %s', devtools.pformat(config.dict())) steps = steps or ALL_STEPS if BuildSteps.extensions in steps: config = apply_modifiers(config, config.extensions.config_modifiers) clean = BuildSteps.clean in steps _empty_dir(config.dist_dir, clean) _empty_dir(config.get_tmp_dir(), clean) pages = None data_future = None with ProcessPoolExecutor() as executor: futures = [ BuildSteps.sass in steps and executor.submit(assets_grablib, config), BuildSteps.webpack in steps and executor.submit(run_webpack, config), ] if BuildSteps.data in steps: data_future = executor.submit(load_data, config) if BuildSteps.pages in steps: pages = build_pages(config) # this will raise errors if any of the above went wrong [f.result() for f in futures if f] som = dict( pages=pages, data=data_future and data_future.result(), config=config, ) if BuildSteps.extensions in steps: apply_page_generator(som, config) som['path_lookup'] = get_path_lookup(config, pages) if BuildSteps.extensions in steps: som = apply_modifiers(som, config.extensions.som_modifiers) if som['pages'] is not None: content_templates(som['pages'].values(), config) render_pages(config, som) return som
def test_skip(): class CustomCls: def __pretty__(self, fmt, skip_exc, **kwargs): raise skip_exc() yield 'Thing()' def __repr__(self): return '<CustomCls repr>' my_cls = CustomCls() v = pformat(my_cls) assert v == '<CustomCls repr>'
async def run_command_method(method_name, method, all_locals: Dict) -> Any: logger.debug(pformat(all_locals)) injected_kwargs = dict( raw_event=all_locals["raw_event"], chain=all_locals["chain"], sender=all_locals["sender"], history=all_locals["status"].history, # context=all_locals["context"], ) if method_name == "cache": injected_kwargs["exception"] = all_locals["exception"] else: if method_name not in COMMAND_LIFECYCLE_EXCEPTIONS.values(): injected_kwargs = {**injected_kwargs, **all_locals["patterns"]} # 参数和group_message/friend_message事件参数一致 logger.debug(f"将被注入 {method_name} 的参数\n{pformat(injected_kwargs)}") result = await await_or_sync(method, **fit_kwargs(method, injected_kwargs)) logger.debug(pformat(result)) return result
async def websocket_receiver(request, ws, protocol: T_BotProtocol): while True: try: data = await ws.recv() raw_event = json.loads(data) logger.debug(pformat(raw_event)) await handle_event(protocol, raw_event) except EventHandleError as e: logger.error(e) except Exception: logger.exception("事件处理异常")
async def http_receiver(request, protocol: T_BotProtocol): try: raw_event = request.json logger.debug(pformat(raw_event)) await handle_event(protocol, raw_event) except EventHandleError as e: logger.error(e) except Exception: logger.exception("事件处理异常") finally: return text("")
def merge_text_of_segments( segments: List[T_SegmentInstance]) -> T_CompressedSegments: """合并相邻的Text片段,空格分隔,方便正则""" logger.debug(pformat(segments)) if len(segments) <= 1: return segments compressed_segments: T_CompressedSegments = [] text_buffer: List[Text] = [] last_segment_type = Text segments_count = len(segments) for index, segment in enumerate(segments, start=1): # True, True if last_segment_type == Text and isinstance(segment, Text): text_buffer.append(segment) if index == segments_count: compressed_segments.append( merge_multi_text_with_space(*text_buffer)) # False, True elif last_segment_type != Text and isinstance(segment, Text): text_buffer.append(segment) if index == segments_count: compressed_segments.append( merge_multi_text_with_space(*text_buffer)) # True, False elif last_segment_type == Text and not isinstance(segment, Text): if text_buffer: compressed_segments.append( merge_multi_text_with_space(*text_buffer)) text_buffer = [] compressed_segments.append(segment) # False, False else: compressed_segments.append(segment) last_segment_type = segment.__class__ return compressed_segments
def test_simple(): class CustomCls: def __pretty__(self, fmt, **kwargs): yield 'Thing(' yield 1 for i in range(3): yield fmt(list(range(i))) yield ',' yield 0 yield -1 yield ')' my_cls = CustomCls() v = pformat(my_cls) assert v == """\
def test_devtools_output_validation_error(): class Model(BaseModel): a: int with pytest.raises(ValueError) as exc_info: Model() assert devtools.pformat( exc_info.value) == ('ValidationError(\n' " model='Model',\n" ' errors=[\n' ' {\n' " 'loc': ('a',),\n" " 'msg': 'field required',\n" " 'type': 'value_error.missing',\n" ' },\n' ' ],\n' ')')
async def measure(self, param: servo.api.MeasureParams) -> Measurement: if isinstance(param, dict): # required parsing has failed in api.Mixin._post_event(), run parse_obj to surface the validation errors servo.api.MeasureParams.parse_obj(param) servo.logger.info(f"Measuring... [metrics={', '.join(param.metrics)}]") servo.logger.trace(devtools.pformat(param)) aggregate_measurement = Measurement.construct() results: list[servo.EventResult] = await self.servo.dispatch_event( servo.Events.measure, metrics=param.metrics, control=param.control ) for result in results: measurement = result.value aggregate_measurement.readings.extend(measurement.readings) aggregate_measurement.annotations.update(measurement.annotations) return aggregate_measurement
def formatMessage(self, record): msg = super().formatMessage(record) if msg[0] != '{': return msg # json from AccessLogger obj = json.loads(msg) if self.stream_is_tty: # in future we can do clever things about colouring the message based on status code msg = '{} {} {}'.format( sformat(obj['time'], sformat.magenta), sformat(obj['prefix'], sformat.blue), sformat(obj['msg'], sformat.dim if obj['dim'] else sformat.reset), ) else: msg = '{time} {prefix} {msg}'.format(**obj) details = getattr(record, 'details', None) if details: msg = 'details: {}\n{}'.format( pformat(details, highlight=self.stream_is_tty), msg) return msg
v = { 'foo': { 'whatever': [3, 2, 1] }, 'sentence': 'hello\nworld', 'generator': (i * 2 for i in [1, 2, 3]), 'matrix': np.matrix([[1, 2, 3, 4], [50, 60, 70, 80], [900, 1000, 1100, 1200], [13000, 14000, 15000, 16000]]) } # pretty print of v pprint(v) # as above without colours, the generator will also be empty as it's already been evaluated s = pformat(v, highlight=False) print(s) pp = PrettyFormat( indent_step=2, # default: 4 indent_char='.', # default: space repr_strings=True, # default: False simple_cutoff= 2, # default: 10 (if repr is below this length it'll be shown on one line) width=80, # default: 120 yield_from_generators=False # default: True (whether to evaluate generators) ) print(pp(v, highlight=True))
def __repr__(self): return pformat(self)
async def exec_command(self) -> servo.api.Status: cmd_response = await self._post_event(servo.api.Events.whats_next, None) self.logger.info(f"What's Next? => {cmd_response.command}") self.logger.trace(devtools.pformat(cmd_response)) if cmd_response.command == servo.api.Commands.describe: description = await self.describe( Control(**cmd_response.param.get("control", {})) ) self.logger.success( f"Described: {len(description.components)} components, {len(description.metrics)} metrics" ) self.logger.debug(devtools.pformat(description)) status = servo.api.Status.ok(descriptor=description.__opsani_repr__()) return await self._post_event(servo.api.Events.describe, status.dict()) elif cmd_response.command == servo.api.Commands.measure: try: measurement = await self.measure(cmd_response.param) self.logger.success( f"Measured: {len(measurement.readings)} readings, {len(measurement.annotations)} annotations" ) self.logger.trace(devtools.pformat(measurement)) param = measurement.__opsani_repr__() except servo.errors.EventError as error: self.logger.error(f"Measurement failed: {error}") param = servo.api.Status.from_error(error).dict() self.logger.error(f"Responding with {param}") self.logger.opt(exception=error).debug("Measure failure details") return await self._post_event(servo.api.Events.measure, param) elif cmd_response.command == servo.api.Commands.adjust: adjustments = servo.api.descriptor_to_adjustments( cmd_response.param["state"] ) control = Control(**cmd_response.param.get("control", {})) try: description = await self.adjust(adjustments, control) status = servo.api.Status.ok(state=description.__opsani_repr__()) components_count = len(description.components) settings_count = sum( len(component.settings) for component in description.components ) self.logger.success( f"Adjusted: {components_count} components, {settings_count} settings" ) except servo.EventError as error: self.logger.error(f"Adjustment failed: {error}") status = servo.api.Status.from_error(error) self.logger.error(f"Responding with {status.dict()}") self.logger.opt(exception=error).debug("Adjust failure details") return await self._post_event(servo.api.Events.adjust, status.dict()) elif cmd_response.command == servo.api.Commands.sleep: # TODO: Model this duration = Duration(cmd_response.param.get("duration", 120)) status = servo.utilities.key_paths.value_for_key_path( cmd_response.param, "data.status", None ) reason = servo.utilities.key_paths.value_for_key_path( cmd_response.param, "data.reason", "unknown reason" ) msg = f"{status}: {reason}" if status else f"{reason}" self.logger.info(f"Sleeping for {duration} ({msg}).") await asyncio.sleep(duration.total_seconds()) # Return a status so we have a simple API contract return servo.api.Status(status="ok", message=msg) else: raise ValueError(f"Unknown command '{cmd_response.command.value}'")
def _repr_pretty_(self, printer, cycle) -> None: """ Display hook for the IPython repl. """ printer.text(pformat(self))
def test_pydantic_pretty(): class MyModel(pydantic.BaseModel): foobar: int = 1 assert pformat(MyModel()) == 'MyModel(\n foobar=1,\n)' assert pformat(MyModel) == "<class 'tests.test_custom_pretty.test_pydantic_pretty.<locals>.MyModel'>"
async def handle_event(protocol: T_BotProtocol, raw_event: Dict): logger.info("*" * 50) if not route_mapping.has_initial: await initial_bot_info() logger.info("成功获取bot元信息") route_mapping.has_initial = True adapter = get_adapter(protocol) raw_event_name = adapter.get_event_name(raw_event) protocol_event_name: str = f"{protocol}_" + raw_event_name if protocol == "onebot": if not onebot_event_meta.has_skip_buffered_event: flag = skip_current_onebot_event(raw_event, raw_event_name) if not flag: return logger.info("onebot 心跳") logger.info(f"{protocol}事件 {raw_event_name}") class_handler_names: Set[str] = set() # 对同一个消息来源,同一个class_handler也只应调用一次 class_command_names: Set[str] = set() # 对同一个消息对象,在处理一次事件时 # 在global_commands、mapping、validator中重复注册的指令应该只运行一次 mode: T_RouteMode source_id: str command_trigger_events: List[str] if protocol_event_name in ALL_GROUP_EVENTS: mode = "group" source_id = get_source_id(protocol, mode, raw_event) command_trigger_events = GROUP_COMMAND_TRIGGER_EVENTS elif protocol_event_name in ALL_PRIVATE_EVENTS: mode = "private" source_id = get_source_id(protocol, mode, raw_event) command_trigger_events = PRIVATE_COMMAND_TRIGGER_EVENTS else: raise EventHandleError(f"无效/尚未实现的事件{protocol_event_name}") validator_handlers, validator_commands = await with_validators(mode, source_id) if protocol_event_name in command_trigger_events: # normal class_command_names |= route_mapping.global_commands[protocol][mode] class_command_names |= route_mapping.mapping[protocol][mode][source_id][ "commands" ] class_command_names |= validator_commands if class_command_names: await run_class_commands( protocol, mode, source_id, raw_event, class_command_names ) # lock_user # for rule_id, rule in lock_user_mapping.items(): # if source_id in rule[protocol]: # await run_class_command(mode="lock_user",rule_id) # if mode =="group" or mode =="channel": # lock_source_mapping # lock_source class_handler_names |= route_mapping.global_handlers[protocol][mode] class_handler_names |= route_mapping.mapping[protocol][mode][source_id][ "class_handlers" ] class_handler_names |= validator_handlers logger.debug(pformat(class_handler_names)) # 比如group_message这样的实现了统一事件的事件 # 如果用户同时定义了group_message和onebot_group_message,应该执行两次 event_mapping = UNIVERSAL_PROTOCOL_EVENT_MAPPING.get(raw_event_name) if event_mapping and protocol_event_name in event_mapping: await run_class_handlers( protocol, mode, source_id, raw_event, raw_event_name, class_handler_names ) await run_class_handlers( protocol, mode, source_id, raw_event, protocol_event_name, class_handler_names )
def test_pretty_not_func(): class Foobar: __pretty__ = 1 assert '<locals>.Foobar object' in pformat(Foobar())
async def parse_pattern( chain: MessageChain, sender: "CommandSender", method_name, cache: CommandMethodCache, prefix: str, context, ): """ 根据签名中的PatternArg,自动解析参数,并转换为对应类型,自动注入函数调用中 满足pattern放行,如果不满足,会对方法调用进行拦截, """ if not cache.compressed_patterns: return {} compressed_patterns = cache.compressed_patterns # todo pydantic有没有原生的功能 # 尝试解析,解析失败,报错 # todo List(展开), Any, Union, List[Union/Any] formot_hint = "请按照 " for arg_name, arg_type in cache.patterns: formot_hint += f"<{arg_name} : {arg_type.__name__}> " formot_hint += "的格式输入\n不需要输入<或者>,:右侧是该参数的类型" # formot_hint添加prefix # if method_name == "initial": results = {} try: compressed_segments = merge_text_of_segments(chain.segments) logger.debug(pformat(compressed_segments)) if len(compressed_segments) != len(compressed_patterns): raise PatternFormotError(f"未提供足够参数,应为{len(cache.patterns)}个," + f"获得{len(chain.segments)}个") # 对initial应用pattern的情况,支持prefix # 目前仅支持文字prefix if method_name == "initial": if not isinstance(compressed_segments[0], Text): return PatternFormotError("目前仅支持文字前缀") with_prefix = compressed_segments[0].content without_prefix = re.sub(f"^{prefix}", "", with_prefix) compressed_segments[0].content = without_prefix results = get_pattern_results(compressed_patterns, compressed_segments) except PatternFormotError as e: # if on_format_error: # return_text = await await_or_normal( # on_format_error, *args, **kwargs # ) # if return_text: # await bot.group_msg(return_text) # else: await sender.send_message( # Text(f"{e}\n{formot_hint if with_formot_hint else ''}") Text(f"{e}\n{formot_hint}")) logger.exception("指令解析失败") raise e else: # todo patternResults的maxSize logger.debug(pformat(results)) return results