update pyglet

fix fpslogger gone bug
start using \viztracer/

modfied git ignore

啊啊啊
This commit is contained in:
shenjack 2022-06-18 17:53:35 +08:00
parent e1fdc5056e
commit 2bc9da9408
48 changed files with 534 additions and 2016 deletions

4
DR.py
View File

@ -4,6 +4,7 @@ mail: 3695888@qq.com
"""
import os
import sys
import time
import cProfile
import traceback
@ -42,6 +43,7 @@ if __name__ == '__main__':
from Difficult_Rocket.api.Exp import *
from Difficult_Rocket.crash import crash
try:
start_time = time.perf_counter_ns()
import pyglet
pyglet.options["win32_gdi_font"] = True
@ -52,6 +54,8 @@ if __name__ == '__main__':
game = main.Game()
print(time.perf_counter_ns() - start_time)
cprofile = False
if cprofile:
cProfile.run('game.start()', sort='calls')

View File

@ -11,24 +11,15 @@ github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
__all__ = ['TexturesError',
'LanguageError',
'TestError']
class Error(Exception):
"""基础 Exception"""
def __bool__(self):
return False
from Difficult_Rocket.api.Exp.main import Error
from Difficult_Rocket.api.Exp.command import CommandError, CommandParseError, CommandQMarkPosError, \
CommandQMarkConflict, CommandQMarkMissing, CommandQMarkPreMissing, CommandQMarkSufMissing
from Difficult_Rocket.api.Exp.unsupport import NoMoreJson5
class TexturesError(Error):
"""材质相关 error"""
class LanguageError(Error):
"""语言相关 error"""
class TestError(Error):
"""就像名字一样 用于测试的 error"""

View File

@ -0,0 +1,15 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2021-2022 by shenjackyuanjie
# All rights reserved
# -------------------------------
from Difficult_Rocket.api.Exp import Error
class LanguageError(Error):
"""语言相关 error"""
class TranslateFileNotFoundError(LanguageError):
"""某个语言的翻译文件未找到"""

View File

@ -0,0 +1,18 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2021-2022 by shenjackyuanjie
# All rights reserved
# -------------------------------
"""
writen by shenjackyuanjie
mail: 3695888@qq.com
github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
class Error(Exception):
"""基础 Exception"""
def __bool__(self):
return False

View File

@ -0,0 +1,22 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2021-2022 by shenjackyuanjie
# All rights reserved
# -------------------------------
"""
writen by shenjackyuanjie
mail: 3695888@qq.com
github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
from Difficult_Rocket.api.Exp.main import Error
class NoMoreJson5(Error):
"""说什么我也不用Json5了"""
class NopeImTeapot418(Error):
"""我只是个茶壶而已,我不能煮咖啡!"""

View File

@ -13,9 +13,7 @@ gitee: @shenjackyuanjie
# system function
import os
import sys
import time
import ctypes
import logging
import traceback
@ -135,7 +133,7 @@ class ClientWindow(Window):
multiline=True,
batch=self.label_batch, group=self.command_group)
# 设置刷新率
pyglet.clock.schedule_interval(self.update, float(self.SPF))
pyglet.clock.schedule(self.draw_update)
# 完成设置后的信息输出
self.logger.info(tr.lang('window', 'setup.done'))
self.logger.info(tr.lang('window', 'os.pid_is').format(os.getpid(), os.getppid()))
@ -143,6 +141,7 @@ class ClientWindow(Window):
self.use_time = end_time - start_time
self.logger.info(tr.lang('window', 'setup.use_time').format(Decimal(self.use_time) / 1000000000))
self.logger.debug(tr.lang('window', 'setup.use_time_ns').format(self.use_time))
self.count = 0
def setup(self):
self.load_fonts()
@ -159,8 +158,8 @@ class ClientWindow(Window):
def start_game(self) -> None:
self.run_input = True
# self.read_input()
pyglet.app.run()
self.read_input()
pyglet.app.event_loop.run(1/self.main_config['runtime']['fps'])
@new_thread('window read_input', daemon=True)
def read_input(self):
@ -190,15 +189,21 @@ class ClientWindow(Window):
draws and some event
"""
def update(self, tick: float):
def draw_update(self, tick: float):
self.count += 1
if self.count >= 100:
try:
self.count = 0
self.logger.debug(tick)
self.logger.debug('update! {} {}'.format(tick, pyglet.clock.get_frequency()))
except ZeroDivisionError:
pass
decimal_tick = Decimal(str(tick)[:10])
self.FPS_update(decimal_tick)
self.fps_log.update_tick(Decimal(tick))
def FPS_update(self, tick: Decimal):
now_FPS = pyglet.clock.get_frequency()
self.fps_log.update_tick(tick)
self.fps_log.update_tick(now_FPS, tick)
self.fps_label.text = f'FPS: {self.fps_log.fps: >5.1f}({self.fps_log.middle_fps: >5.1f})[{now_FPS: >.7f}]\n {self.fps_log.max_fps: >7.1f} {self.fps_log.min_fps:>5.1f}'
def on_draw(self, *dt):
@ -210,6 +215,9 @@ class ClientWindow(Window):
super().on_resize(width, height)
self.fps_label.y = height - 10
def on_refresh(self, dt):
...
def draw_batch(self):
self.part_batch.draw()
self.label_batch.draw()
@ -221,6 +229,9 @@ class ClientWindow(Window):
def on_command(self, command: line.CommandText):
self.logger.info(tr.lang('window', 'command.text').format(command))
if command.match('stop'):
self.is_running = False
self.dispatch_event('on_exit')
platform_event_loop.stop()
self.dispatch_event('on_close', 'command') # source = command
elif command.match('fps'):
if command.match('log'):

View File

@ -17,8 +17,6 @@ import statistics
from typing import Union
from decimal import Decimal
from libs.pyglet.clock import get_frequency
class FpsLogger:
def __init__(self,
@ -34,12 +32,15 @@ class FpsLogger:
self._min_fps = stable_fps
def update_tick(self,
pyglet_fps: float,
tick: Decimal):
now_fps = get_frequency()
if now_fps != 0:
self.fps_list.append(now_fps)
if pyglet_fps != 0:
self.fps_list.append(pyglet_fps)
else:
self.fps_list.append(1)
if tick != 0:
self.fps_list.append(float(1 / tick))
else:
self.fps_list.append(1)
if len(self.fps_list) > self.count:
self.fps_list = self.fps_list[-self.count + 1:] # 整个列表往前挪一位
if len(self.get_fps_list) > self.count:

View File

@ -14,6 +14,11 @@ gitee: @shenjackyuanjie
from Difficult_Rocket import game_version
from Difficult_Rocket.command import line
class Command:
"""一个空类 用于标记命令信息"""
COMMAND = 'command'
SUB_COMMAND = 'sub_command'
INFO = 'info'

View File

@ -18,13 +18,15 @@ import logging
import logging.config
import multiprocessing
import pyglet.clock
if __name__ == '__main__': # been start will not run this
sys.path.append('/bin/libs')
sys.path.append('/bin')
from Difficult_Rocket import client, server
from utils import tools
from utils.translate import tr
from Difficult_Rocket.utils import tools
from Difficult_Rocket.utils.translate import tr
class Game:
@ -37,7 +39,7 @@ class Game:
self.language = tools.load_file('configs/main.config', 'runtime')['language']
tr.set_language(self.language)
# logging config
log_config = tools.load_file('configs/logger.json5')
log_config = tools.load_file('configs/logger.toml')
file_name = log_config['handlers']['file']['filename']
del log_config['handlers']['file']['datefmt']
log_config['handlers']['file']['filename'] = f'logs/{file_name.format(self.start_time)}'
@ -56,6 +58,7 @@ class Game:
def setup(self) -> None:
self.client = client.Client(net_mode='local')
pyglet.clock.schedule(self.client.window.draw_update)
self.server = server.Server(net_mode='local')
def python_version_check(self) -> None: # best 3.8+ and write at 3.8.10

View File

@ -43,5 +43,6 @@ class Server:
self.net_mode = net_mode
self.logger.info(tr.lang('server', 'setup.done'))
@new_thread('Server')
def run(self):
self.logger.info(tr.lang('server', 'os.pid_is').format(os.getpid(), os.getppid()))

View File

@ -15,20 +15,14 @@ import os
import sys
import time
import math
import decimal
import logging
import configparser
from xml.dom.minidom import parse
if __name__ == '__main__': # 如果是直接运行该文件,则将工作目录切换到该文件所在目录
sys.path.append('./libs')
sys.path.append('./')
import toml
from libs import json5
from libs import toml
from Difficult_Rocket.api.Exp import NoMoreJson5
# logger
tools_logger = logging.getLogger('part-tools')
"""
@ -44,17 +38,7 @@ def load_file(file_name: str, stack=None):
f_type = file_name[file_name.rfind('.') + 1:] # 从最后一个.到末尾 (截取文件格式)
try:
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
if (f_type == 'json5') or (f_type == 'json'):
try:
with open(file_name, 'r', encoding='utf-8') as jf: # jf -> json file
get_file = json5.load(jf, encoding='uft-8')
except UnicodeDecodeError:
with open(file_name, 'r', encoding='gbk') as jf:
get_file = json5.load(jf)
tools_logger.info('文件 %s 解码错误已重新使用gbk编码打开' % file_name)
if stack is not None:
get_file = get_file[stack]
elif f_type == 'xml':
if f_type == 'xml':
xml_load = parse(file_name)
if stack is not None:
get_file = xml_load.getElementsByTagName(stack)
@ -65,6 +49,8 @@ def load_file(file_name: str, stack=None):
get_file = get_file[stack]
elif f_type == 'toml':
get_file = toml.load(file_name)
elif f_type == 'json5':
raise NoMoreJson5("我说什么也不用json5了喵的")
except Exception as exp:
error_type = type(exp).__name__
if error_type in file_error:
@ -83,7 +69,7 @@ def get_At(name, in_xml, need_type=str):
"""
get Attribute from a XML tree
will raise TypeError if input is not str or list
XML no! Json5 yes!
XML json5 no! toml yes!
"""
name_type = type(name)
if name_type == list:

View File

@ -3,7 +3,7 @@
IFS=$'\n';
# 默认值是 10个 可以一次性展示更多 就修改第15行代码 | head -n 30 或者更多
objects=`git verify-pack -v .git/objects/pack/pack-aba4bfc55979194c86dbd466c86e57d8199ae7ad.idx | grep -v chain | sort -k3nr | head -n 50`
objects=`git verify-pack -v .git/objects/pack/pack-aba4bfc55979194c86dbd466c86e57d8199ae7ad.idx | grep -v chain | sort -k3nr | head -n 100`
echo "All sizes are in kB. The pack column is the size of the object, compressed, inside the pack file."
@ -23,3 +23,5 @@ do
done
echo -e $output | column -t -s ', '
pause

View File

@ -1,7 +1,7 @@
{
'version': 1,
'formatters': {
'file': { // file 跟 root 的输出格式一致
'file': {
'class': 'logging.Formatter',
'format': '[%(asctime)s][%(name)s]:[%(levelname)s] %(message)s',
'datefmt': '%Y-%m-%d %H-%M-%S'

46
configs/logger.toml Normal file
View File

@ -0,0 +1,46 @@
version = 1
[root]
level = "DEBUG"
handlers = [ "console", "file"]
[formatters.file]
class = "logging.Formatter"
format = "[%(asctime)s][%(name)s]:[%(levelname)s] %(message)s"
datefmt = "%Y-%m-%d %H-%M-%S"
[formatters.client]
class = "logging.Formatter"
format = "[%(asctime)s][%(name)s]:[%(levelname)s] %(message)s"
datefmt = "%Y-%m-%d %H-%M-%S"
[formatters.server]
class = "logging.Formatter"
format = "[%(asctime)s][%(name)s]:[%(levelname)s] %(message)s"
datefmt = "%Y-%m-%d %H-%M-%S"
[handlers.console]
class = "logging.StreamHandler"
formatter = "file"
level = "DEBUG"
[handlers.file]
class = "logging.FileHandler"
filename = "{} DR.log"
datefmt = "%Y-%m-%d %H-%M-%S"
encoding = "utf-8"
formatter = "file"
level = "DEBUG"
mode = "w"
[loggers.client]
level = "DEBUG"
handlers = []
[loggers.server]
level = "DEBUG"
handlers = []
[loggers.main]
level = "DEBUG"
handlers = []

View File

@ -8,8 +8,8 @@ fonts_folder = "libs/fonts"
[window]
style = "None"
width = 1300
height = 931
width = 1139
height = 1145
visible = true
caption = "Difficult Rocket {version}"
resizable = true

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed 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.

View File

@ -1,26 +0,0 @@
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed 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.
"""A pure Python implementation of the JSON5 configuration language."""
from .lib import load, loads, dump, dumps
from .version import VERSION
__all__ = [
'VERSION',
'dump',
'dumps',
'load',
'loads',
]

View File

@ -1,20 +0,0 @@
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed 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.
import sys # pragma: no cover
from .tool import main # pragma: no cover
if __name__ == '__main__': # pragma: no cover
sys.exit(main())

View File

@ -1,61 +0,0 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed 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.
import argparse
class _Bailout(Exception):
pass
class ArgumentParser(argparse.ArgumentParser):
SUPPRESS = argparse.SUPPRESS
def __init__(self, host, prog, desc, **kwargs):
kwargs['prog'] = prog
kwargs['description'] = desc
kwargs['formatter_class'] = argparse.RawDescriptionHelpFormatter
super(ArgumentParser, self).__init__(**kwargs)
self._host = host
self.exit_status = None
self.add_argument('-V', '--version', action='store_true',
help='print the version and exit')
def parse_args(self, args=None, namespace=None):
try:
rargs = super(ArgumentParser, self).parse_args(args=args,
namespace=namespace)
except _Bailout:
return None
return rargs
# Redefining built-in 'file' pylint: disable=W0622
def _print_message(self, msg, file=None):
self._host.print_(msg=msg, stream=file, end='\n')
def print_help(self, file=None):
self._print_message(msg=self.format_help(), file=file)
def error(self, message, bailout=True): # pylint: disable=W0221
self.exit(2, '%s: error: %s\n' % (self.prog, message), bailout=bailout)
def exit(self, status=0, message=None, # pylint: disable=W0221
bailout=True):
self.exit_status = status
if message:
self._print_message(message, file=self._host.stderr)
if bailout:
raise _Bailout()

View File

@ -1,57 +0,0 @@
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed 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.
import os
import shutil
import sys
import tempfile
if sys.version_info[0] < 3:
# pylint: disable=redefined-builtin, invalid-name
str = unicode
class Host(object):
def __init__(self):
self.stdin = sys.stdin
self.stdout = sys.stdout
self.stderr = sys.stderr
def chdir(self, *comps):
return os.chdir(self.join(*comps))
def getcwd(self):
return os.getcwd()
def join(self, *comps):
return os.path.join(*comps)
def mkdtemp(self, **kwargs):
return tempfile.mkdtemp(**kwargs)
def print_(self, msg=u'', end=u'\n', stream=None):
stream = stream or self.stdout
stream.write(str(msg) + end)
stream.flush()
def rmtree(self, path):
shutil.rmtree(path, ignore_errors=True)
def read_text_file(self, path):
with open(path, 'rb') as fp:
return fp.read().decode('utf8')
def write_text_file(self, path, contents):
with open(path, 'wb') as f:
f.write(contents.encode('utf8'))

View File

@ -1,511 +0,0 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed 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.
import math
import re
import sys
import unicodedata
from .parser import Parser
if sys.version_info[0] < 3:
str_types = (str, unicode)
str = unicode # pylint: disable=redefined-builtin, invalid-name
else:
str_types = (str,)
long = int # pylint: disable=redefined-builtin, invalid-name
def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, object_pairs_hook=None,
allow_duplicate_keys=True):
"""Deserialize ``fp`` (a ``.read()``-supporting file-like object
containing a JSON document) to a Python object.
Supports almost the same arguments as ``json.load()`` except that:
- the `cls` keyword is ignored.
- an extra `allow_duplicate_keys` parameter supports checking for
duplicate keys in a object; by default, this is True for
compatibility with ``json.load()``, but if set to False and
the object contains duplicate keys, a ValueError will be raised.
"""
s = fp.read()
return loads(s, encoding=encoding, cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant,
object_pairs_hook=object_pairs_hook,
allow_duplicate_keys=allow_duplicate_keys)
def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, object_pairs_hook=None,
allow_duplicate_keys=True):
"""Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a
JSON5 document) to a Python object.
Supports the same arguments as ``json.load()`` except that:
- the `cls` keyword is ignored.
- an extra `allow_duplicate_keys` parameter supports checking for
duplicate keys in a object; by default, this is True for
compatibility with ``json.load()``, but if set to False and
the object contains duplicate keys, a ValueError will be raised.
"""
assert cls is None, 'Custom decoders are not supported'
if sys.version_info[0] < 3:
decodable_type = type('')
else:
decodable_type = type(b'')
if isinstance(s, decodable_type):
encoding = encoding or 'utf-8'
s = s.decode(encoding)
if not s:
raise ValueError('Empty strings are not legal JSON5')
parser = Parser(s, '<string>')
ast, err, _ = parser.parse()
if err:
raise ValueError(err)
def _fp_constant_parser(s):
return float(s.replace('Infinity', 'inf').replace('NaN', 'nan'))
if object_pairs_hook:
dictify = object_pairs_hook
elif object_hook:
dictify = lambda pairs: object_hook(dict(pairs))
else:
dictify = lambda pairs: dict(pairs) # pylint: disable=unnecessary-lambda
if not allow_duplicate_keys:
_orig_dictify = dictify
dictify = lambda pairs: _reject_duplicate_keys(pairs, _orig_dictify)
parse_float = parse_float or float
parse_int = parse_int or int
parse_constant = parse_constant or _fp_constant_parser
return _walk_ast(ast, dictify, parse_float, parse_int, parse_constant)
def _reject_duplicate_keys(pairs, dictify):
keys = set()
for key, _ in pairs:
if key in keys:
raise ValueError('Duplicate key "%s" found in object', key)
keys.add(key)
return dictify(pairs)
def _walk_ast(el, dictify, parse_float, parse_int, parse_constant):
if el == 'None':
return None
if el == 'True':
return True
if el == 'False':
return False
ty, v = el
if ty == 'number':
if v.startswith('0x') or v.startswith('0X'):
return parse_int(v, base=16)
elif '.' in v or 'e' in v or 'E' in v:
return parse_float(v)
elif 'Infinity' in v or 'NaN' in v:
return parse_constant(v)
else:
return parse_int(v)
if ty == 'string':
return v
if ty == 'object':
pairs = []
for key, val_expr in v:
val = _walk_ast(val_expr, dictify, parse_float, parse_int,
parse_constant)
pairs.append((key, val))
return dictify(pairs)
if ty == 'array':
return [_walk_ast(el, dictify, parse_float, parse_int, parse_constant)
for el in v]
raise Exception('unknown el: ' + el) # pragma: no cover
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False,
quote_keys=False, trailing_commas=True,
allow_duplicate_keys=True,
**kwargs):
"""Serialize ``obj`` to a JSON5-formatted stream to ``fp`` (a ``.write()``-
supporting file-like object).
Supports the same arguments as ``json.dumps()``, except that:
- The ``cls`` keyword is not supported.
- The ``encoding`` keyword is ignored; Unicode strings are always written.
- By default, object keys that are legal identifiers are not quoted;
if you pass quote_keys=True, they will be.
- By default, if lists and objects span multiple lines of output (i.e.,
when ``indent`` >=0), the last item will have a trailing comma
after it. If you pass ``trailing_commas=False, it will not.
- If you use a number, a boolean, or None as a key value in a dict,
it will be converted to the corresponding json string value, e.g.
"1", "true", or "null". By default, dump() will match the `json`
modules behavior and produce ill-formed JSON if you mix keys of
different types that have the same converted value, e.g.:
{1: "foo", "1": "bar"} produces '{"1": "foo", "1": "bar"}', an
object with duplicated keys. If you pass allow_duplicate_keys=False,
an exception will be raised instead.
Calling ``dumps(obj, fp, quote_keys=True, trailing_commas=False,
allow_duplicate_keys=True)``
should produce exactly the same output as ``json.dumps(obj, fp).``
"""
fp.write(str(dumps(obj=obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan,
cls=cls, indent=indent, separators=separators,
default=default, sort_keys=sort_keys,
quote_keys=quote_keys, trailing_commas=trailing_commas,
allow_duplicate_keys=allow_duplicate_keys)))
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False,
quote_keys=False, trailing_commas=True, allow_duplicate_keys=True,
**kwargs):
"""Serialize ``obj`` to a JSON5-formatted ``str``.
Supports the same arguments as ``json.dumps()``, except that:
- The ``cls`` keyword is not supported.
- The ``encoding`` keyword is ignored; Unicode strings are always returned.
- By default, object keys that are legal identifiers are not quoted;
if you pass quote_keys=True, they will be.
- By default, if lists and objects span multiple lines of output (i.e.,
when ``indent`` >=0), the last item will have a trailing comma
after it. If you pass ``trailing_commas=False, it will not.
- If you use a number, a boolean, or None as a key value in a dict,
it will be converted to the corresponding json string value, e.g.
"1", "true", or "null". By default, dump() will match the ``json``
module's behavior and produce ill-formed JSON if you mix keys of
different types that have the same converted value, e.g.:
{1: "foo", "1": "bar"} produces '{"1": "foo", "1": "bar"}', an
object with duplicated keys. If you pass ``allow_duplicate_keys=False``,
an exception will be raised instead.
Calling ``dumps(obj, quote_keys=True, trailing_commas=False,
allow_duplicate_keys=True)``
should produce exactly the same output as ``json.dumps(obj).``
"""
assert kwargs.get('cls', None) is None, 'Custom encoders are not supported'
if separators is None:
if indent is None:
separators = (u', ', u': ')
else:
separators = (u',', u': ')
default = default or _raise_type_error
if check_circular:
seen = set()
else:
seen = None
level = 1
is_key = False
_, v = _dumps(obj, skipkeys, ensure_ascii, check_circular,
allow_nan, indent, separators, default, sort_keys,
quote_keys, trailing_commas, allow_duplicate_keys,
seen, level, is_key)
return v
def _dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, indent,
separators, default, sort_keys,
quote_keys, trailing_commas, allow_duplicate_keys,
seen, level, is_key):
if obj is True:
s = u'true'
elif obj is False:
s = u'false'
elif obj is None:
s = u'null'
elif isinstance(obj, str_types):
if (is_key and _is_ident(obj) and not quote_keys
and not _is_reserved_word(obj)):
return True, obj
return True, _dump_str(obj, ensure_ascii)
elif isinstance(obj, float):
s = _dump_float(obj, allow_nan)
elif isinstance(obj, int):
s = str(obj)
else:
s = None
if is_key:
if s is not None:
return True, '"%s"' % s
if skipkeys:
return False, None
raise TypeError('invalid key %s' % repr(obj))
if s is not None:
return True, s
if indent is not None:
end_str = ''
if trailing_commas:
end_str = ','
if type(indent) == int:
if indent > 0:
indent_str = '\n' + ' ' * indent * level
end_str += '\n' + ' ' * indent * (level - 1)
else:
indent_str = '\n'
end_str += '\n'
else:
indent_str = '\n' + indent * level
end_str += '\n' + indent * (level - 1)
else:
indent_str = ''
end_str = ''
item_sep, kv_sep = separators
item_sep += indent_str
level += 1
if seen is not None:
i = id(obj)
if i in seen:
raise ValueError('Circular reference detected.')
else:
seen.add(i)
# In Python3, we'd check if this was an abc.Mapping or an abc.Sequence.
# For now, just check for the attrs we need to iterate over the object.
if hasattr(obj, 'keys') and hasattr(obj, '__getitem__'):
s = _dump_dict(obj, skipkeys, ensure_ascii,
check_circular, allow_nan, indent,
separators, default, sort_keys,
quote_keys, trailing_commas,
allow_duplicate_keys, seen, level,
item_sep, kv_sep, indent_str, end_str)
elif hasattr(obj, '__getitem__') and hasattr(obj, '__iter__'):
s = _dump_array(obj, skipkeys, ensure_ascii,
check_circular, allow_nan, indent,
separators, default, sort_keys,
quote_keys, trailing_commas,
allow_duplicate_keys, seen, level,
item_sep, indent_str, end_str)
else:
s = default(obj)
if seen is not None:
seen.remove(i)
return False, s
def _dump_dict(obj, skipkeys, ensure_ascii, check_circular, allow_nan,
indent, separators, default, sort_keys,
quote_keys, trailing_commas, allow_duplicate_keys,
seen, level, item_sep, kv_sep, indent_str, end_str):
if not obj:
return u'{}'
if sort_keys:
keys = sorted(obj.keys())
else:
keys = obj.keys()
s = u'{' + indent_str
num_items_added = 0
new_keys = set()
for key in keys:
valid_key, key_str = _dumps(key, skipkeys, ensure_ascii, check_circular,
allow_nan, indent, separators, default,
sort_keys,
quote_keys, trailing_commas,
allow_duplicate_keys,
seen, level, is_key=True)
if valid_key:
if not allow_duplicate_keys:
if key_str in new_keys:
raise ValueError('duplicate key %s' % repr(key))
else:
new_keys.add(key_str)
if num_items_added:
s += item_sep
s += key_str + kv_sep + _dumps(obj[key], skipkeys, ensure_ascii,
check_circular, allow_nan, indent,
separators, default, sort_keys,
quote_keys, trailing_commas,
allow_duplicate_keys,
seen, level, is_key=False)[1]
num_items_added += 1
elif not skipkeys:
raise TypeError('invalid key %s' % repr(key))
s += end_str + u'}'
return s
def _dump_array(obj, skipkeys, ensure_ascii, check_circular, allow_nan,
indent, separators, default, sort_keys,
quote_keys, trailing_commas, allow_duplicate_keys,
seen, level, item_sep, indent_str, end_str):
if not obj:
return u'[]'
return (u'[' + indent_str +
item_sep.join([_dumps(el, skipkeys, ensure_ascii, check_circular,
allow_nan, indent, separators, default,
sort_keys, quote_keys, trailing_commas,
allow_duplicate_keys,
seen, level, False)[1] for el in obj]) +
end_str + u']')
def _dump_float(obj, allow_nan):
if allow_nan:
if math.isnan(obj):
return 'NaN'
if obj == float('inf'):
return 'Infinity'
if obj == float('-inf'):
return '-Infinity'
elif math.isnan(obj) or obj == float('inf') or obj == float('-inf'):
raise ValueError('Out of range float values '
'are not JSON compliant')
return str(obj)
def _dump_str(obj, ensure_ascii):
ret = ['"']
for ch in obj:
if ch == '\\':
ret.append('\\\\')
elif ch == '"':
ret.append('\\"')
elif ch == u'\u2028':
ret.append('\\u2028')
elif ch == u'\u2029':
ret.append('\\u2029')
elif ch == '\n':
ret.append('\\n')
elif ch == '\r':
ret.append('\\r')
elif ch == '\b':
ret.append('\\b')
elif ch == '\f':
ret.append('\\f')
elif ch == '\t':
ret.append('\\t')
elif ch == '\v':
ret.append('\\v')
elif ch == '\0':
ret.append('\\0')
elif not ensure_ascii:
ret.append(ch)
else:
o = ord(ch)
if o >= 32 and o < 128:
ret.append(ch)
elif o < 65536:
ret.append('\\u' + '%04x' % o)
else:
val = o - 0x10000
high = 0xd800 + (val >> 10)
low = 0xdc00 + (val & 0x3ff)
ret.append('\\u%04x\\u%04x' % (high, low))
return u''.join(ret) + '"'
def _is_ident(k):
k = str(k)
if not k or not _is_id_start(k[0]) and k[0] not in (u'$', u'_'):
return False
for ch in k[1:]:
if not _is_id_continue(ch) and ch not in (u'$', u'_'):
return False
return True
def _is_id_start(ch):
return unicodedata.category(ch) in (
'Lu', 'Ll', 'Li', 'Lt', 'Lm', 'Lo', 'Nl')
def _is_id_continue(ch):
return unicodedata.category(ch) in (
'Lu', 'Ll', 'Li', 'Lt', 'Lm', 'Lo', 'Nl', 'Nd', 'Mn', 'Mc', 'Pc')
_reserved_word_re = None
def _is_reserved_word(k):
global _reserved_word_re
if _reserved_word_re is None:
# List taken from section 7.6.1 of ECMA-262.
_reserved_word_re = re.compile('(' + '|'.join([
'break',
'case',
'catch',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'enum',
'export',
'extends',
'false',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'new',
'null',
'return',
'super',
'switch',
'this',
'throw',
'true',
'try',
'typeof',
'var',
'void',
'while',
'with',
]) + ')$')
return _reserved_word_re.match(k) is not None
def _raise_type_error(obj):
raise TypeError('%s is not JSON5 serializable' % repr(obj))

View File

@ -1,894 +0,0 @@
# pylint: disable=line-too-long,unnecessary-lambda
import sys
if sys.version_info[0] < 3:
# pylint: disable=redefined-builtin,invalid-name
chr = unichr
range = xrange
str = unicode
class Parser(object):
def __init__(self, msg, fname):
self.msg = str(msg)
self.end = len(self.msg)
self.fname = fname
self.val = None
self.pos = 0
self.failed = False
self.errpos = 0
self._scopes = []
self._cache = {}
def parse(self):
self._grammar_()
if self.failed:
return None, self._err_str(), self.errpos
return self.val, None, self.pos
def _err_str(self):
lineno, colno = self._err_offsets()
if self.errpos == len(self.msg):
thing = 'end of input'
else:
thing = '"%s"' % self.msg[self.errpos]
return '%s:%d Unexpected %s at column %d' % (
self.fname, lineno, thing, colno)
def _err_offsets(self):
lineno = 1
colno = 1
for i in range(self.errpos):
if self.msg[i] == '\n':
lineno += 1
colno = 1
else:
colno += 1
return lineno, colno
def _succeed(self, v, newpos=None):
self.val = v
self.failed = False
if newpos is not None:
self.pos = newpos
def _fail(self):
self.val = None
self.failed = True
if self.pos >= self.errpos:
self.errpos = self.pos
def _rewind(self, newpos):
self._succeed(None, newpos)
def _bind(self, rule, var):
rule()
if not self.failed:
self._set(var, self.val)
def _not(self, rule):
p = self.pos
rule()
if self.failed:
self._succeed(None, p)
else:
self._rewind(p)
self._fail()
def _opt(self, rule):
p = self.pos
rule()
if self.failed:
self._succeed([], p)
else:
self._succeed([self.val])
def _plus(self, rule):
vs = []
rule()
vs.append(self.val)
if self.failed:
return
self._star(rule, vs)
def _star(self, rule, vs=None):
vs = vs or []
while not self.failed:
p = self.pos
rule()
if self.failed:
self._rewind(p)
if p < self.errpos:
self.errpos = p
break
else:
vs.append(self.val)
self._succeed(vs)
def _seq(self, rules):
for rule in rules:
rule()
if self.failed:
return
def _choose(self, rules):
p = self.pos
for rule in rules[:-1]:
rule()
if not self.failed:
return
self._rewind(p)
rules[-1]()
def _ch(self, ch):
p = self.pos
if p < self.end and self.msg[p] == ch:
self._succeed(ch, self.pos + 1)
else:
self._fail()
def _str(self, s):
for ch in s:
self._ch(ch)
if self.failed:
return
self.val = s
def _range(self, i, j):
p = self.pos
if p != self.end and ord(i) <= ord(self.msg[p]) <= ord(j):
self._succeed(self.msg[p], self.pos + 1)
else:
self._fail()
def _push(self, name):
self._scopes.append((name, {}))
def _pop(self, name):
actual_name, _ = self._scopes.pop()
assert name == actual_name
def _get(self, var):
return self._scopes[-1][1][var]
def _set(self, var, val):
self._scopes[-1][1][var] = val
def _is_unicat(self, var, cat):
import unicodedata
return unicodedata.category(var) == cat
def _join(self, s, vs):
return s.join(vs)
def _xtou(self, s):
return chr(int(s, base=16))
def _grammar_(self):
self._push('grammar')
self._seq([self._sp_, lambda: self._bind(self._value_, 'v'), self._sp_,
self._end_, lambda: self._succeed(self._get('v'))])
self._pop('grammar')
def _sp_(self):
self._star(self._ws_)
def _ws_(self):
self._choose([self._ws__c0_, self._eol_, self._comment_, self._ws__c3_,
self._ws__c4_, self._ws__c5_, self._ws__c6_,
self._ws__c7_, self._ws__c8_])
def _ws__c0_(self):
self._ch(' ')
def _ws__c3_(self):
self._ch('\t')
def _ws__c4_(self):
self._ch('\v')
def _ws__c5_(self):
self._ch('\f')
def _ws__c6_(self):
self._ch(u'\xa0')
def _ws__c7_(self):
self._ch(u'\ufeff')
def _ws__c8_(self):
self._push('ws__c8')
self._seq([lambda: self._bind(self._anything_, 'x'), self._ws__c8__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('ws__c8')
def _ws__c8__s1_(self):
v = self._is_unicat(self._get('x'), 'Zs')
if v:
self._succeed(v)
else:
self._fail()
def _eol_(self):
self._choose([self._eol__c0_, self._eol__c1_, self._eol__c2_,
self._eol__c3_, self._eol__c4_])
def _eol__c0_(self):
self._seq([lambda: self._ch('\r'), lambda: self._ch('\n')])
def _eol__c1_(self):
self._ch('\r')
def _eol__c2_(self):
self._ch('\n')
def _eol__c3_(self):
self._ch(u'\u2028')
def _eol__c4_(self):
self._ch(u'\u2029')
def _comment_(self):
self._choose([self._comment__c0_, self._comment__c1_])
def _comment__c0_(self):
self._seq([lambda: self._str('//'),
lambda: self._star(self._comment__c0__s1_p_)])
def _comment__c0__s1_p_(self):
self._seq([lambda: self._not(self._eol_), self._anything_])
def _comment__c1_(self):
self._seq([lambda: self._str('/*'), self._comment__c1__s1_,
lambda: self._str('*/')])
def _comment__c1__s1_(self):
self._star(lambda: self._seq([self._comment__c1__s1_p__s0_, self._anything_]))
def _comment__c1__s1_p__s0_(self):
self._not(lambda: self._str('*/'))
def _value_(self):
self._choose([self._value__c0_, self._value__c1_, self._value__c2_,
self._value__c3_, self._value__c4_, self._value__c5_,
self._value__c6_])
def _value__c0_(self):
self._seq([lambda: self._str('null'), lambda: self._succeed('None')])
def _value__c1_(self):
self._seq([lambda: self._str('true'), lambda: self._succeed('True')])
def _value__c2_(self):
self._seq([lambda: self._str('false'), lambda: self._succeed('False')])
def _value__c3_(self):
self._push('value__c3')
self._seq([lambda: self._bind(self._object_, 'v'),
lambda: self._succeed(['object', self._get('v')])])
self._pop('value__c3')
def _value__c4_(self):
self._push('value__c4')
self._seq([lambda: self._bind(self._array_, 'v'),
lambda: self._succeed(['array', self._get('v')])])
self._pop('value__c4')
def _value__c5_(self):
self._push('value__c5')
self._seq([lambda: self._bind(self._string_, 'v'),
lambda: self._succeed(['string', self._get('v')])])
self._pop('value__c5')
def _value__c6_(self):
self._push('value__c6')
self._seq([lambda: self._bind(self._num_literal_, 'v'),
lambda: self._succeed(['number', self._get('v')])])
self._pop('value__c6')
def _object_(self):
self._choose([self._object__c0_, self._object__c1_])
def _object__c0_(self):
self._push('object__c0')
self._seq([lambda: self._ch('{'), self._sp_,
lambda: self._bind(self._member_list_, 'v'), self._sp_,
lambda: self._ch('}'), lambda: self._succeed(self._get('v'))])
self._pop('object__c0')
def _object__c1_(self):
self._seq([lambda: self._ch('{'), self._sp_, lambda: self._ch('}'),
lambda: self._succeed([])])
def _array_(self):
self._choose([self._array__c0_, self._array__c1_])
def _array__c0_(self):
self._push('array__c0')
self._seq([lambda: self._ch('['), self._sp_,
lambda: self._bind(self._element_list_, 'v'), self._sp_,
lambda: self._ch(']'), lambda: self._succeed(self._get('v'))])
self._pop('array__c0')
def _array__c1_(self):
self._seq([lambda: self._ch('['), self._sp_, lambda: self._ch(']'),
lambda: self._succeed([])])
def _string_(self):
self._choose([self._string__c0_, self._string__c1_])
def _string__c0_(self):
self._push('string__c0')
self._seq([self._squote_, self._string__c0__s1_, self._squote_,
lambda: self._succeed(self._join('', self._get('cs')))])
self._pop('string__c0')
def _string__c0__s1_(self):
self._bind(lambda: self._star(self._sqchar_), 'cs')
def _string__c1_(self):
self._push('string__c1')
self._seq([self._dquote_, self._string__c1__s1_, self._dquote_,
lambda: self._succeed(self._join('', self._get('cs')))])
self._pop('string__c1')
def _string__c1__s1_(self):
self._bind(lambda: self._star(self._dqchar_), 'cs')
def _sqchar_(self):
self._choose([self._sqchar__c0_, self._sqchar__c1_, self._sqchar__c2_])
def _sqchar__c0_(self):
self._push('sqchar__c0')
self._seq([self._bslash_, lambda: self._bind(self._esc_char_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('sqchar__c0')
def _sqchar__c1_(self):
self._seq([self._bslash_, self._eol_, lambda: self._succeed('')])
def _sqchar__c2_(self):
self._push('sqchar__c2')
self._seq([lambda: self._not(self._bslash_),
lambda: self._not(self._squote_),
lambda: self._not(self._eol_),
lambda: self._bind(self._anything_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('sqchar__c2')
def _dqchar_(self):
self._choose([self._dqchar__c0_, self._dqchar__c1_, self._dqchar__c2_])
def _dqchar__c0_(self):
self._push('dqchar__c0')
self._seq([self._bslash_, lambda: self._bind(self._esc_char_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('dqchar__c0')
def _dqchar__c1_(self):
self._seq([self._bslash_, self._eol_, lambda: self._succeed('')])
def _dqchar__c2_(self):
self._push('dqchar__c2')
self._seq([lambda: self._not(self._bslash_),
lambda: self._not(self._dquote_),
lambda: self._not(self._eol_),
lambda: self._bind(self._anything_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('dqchar__c2')
def _bslash_(self):
self._ch('\\')
def _squote_(self):
self._ch("'")
def _dquote_(self):
self._ch('"')
def _esc_char_(self):
self._choose([self._esc_char__c0_, self._esc_char__c1_,
self._esc_char__c2_, self._esc_char__c3_,
self._esc_char__c4_, self._esc_char__c5_,
self._esc_char__c6_, self._esc_char__c7_,
self._esc_char__c8_, self._esc_char__c9_,
self._esc_char__c10_, self._esc_char__c11_,
self._esc_char__c12_])
def _esc_char__c0_(self):
self._seq([lambda: self._ch('b'), lambda: self._succeed('\b')])
def _esc_char__c1_(self):
self._seq([lambda: self._ch('f'), lambda: self._succeed('\f')])
def _esc_char__c10_(self):
self._seq([lambda: self._ch('0'), lambda: self._not(self._digit_),
lambda: self._succeed('\x00')])
def _esc_char__c11_(self):
self._push('esc_char__c11')
self._seq([lambda: self._bind(self._hex_esc_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('esc_char__c11')
def _esc_char__c12_(self):
self._push('esc_char__c12')
self._seq([lambda: self._bind(self._unicode_esc_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('esc_char__c12')
def _esc_char__c2_(self):
self._seq([lambda: self._ch('n'), lambda: self._succeed('\n')])
def _esc_char__c3_(self):
self._seq([lambda: self._ch('r'), lambda: self._succeed('\r')])
def _esc_char__c4_(self):
self._seq([lambda: self._ch('t'), lambda: self._succeed('\t')])
def _esc_char__c5_(self):
self._seq([lambda: self._ch('v'), lambda: self._succeed('\v')])
def _esc_char__c6_(self):
self._seq([self._squote_, lambda: self._succeed("'")])
def _esc_char__c7_(self):
self._seq([self._dquote_, lambda: self._succeed('"')])
def _esc_char__c8_(self):
self._seq([self._bslash_, lambda: self._succeed('\\')])
def _esc_char__c9_(self):
self._push('esc_char__c9')
self._seq([self._esc_char__c9__s0_,
lambda: self._bind(self._anything_, 'c'),
lambda: self._succeed(self._get('c'))])
self._pop('esc_char__c9')
def _esc_char__c9__s0_(self):
self._not(lambda: (self._esc_char__c9__s0_n_g_)())
def _esc_char__c9__s0_n_g_(self):
self._choose([self._esc_char__c9__s0_n_g__c0_,
self._esc_char__c9__s0_n_g__c1_,
lambda: self._seq([self._digit_]),
lambda: self._seq([self._eol_])])
def _esc_char__c9__s0_n_g__c0_(self):
self._seq([lambda: self._ch('x')])
def _esc_char__c9__s0_n_g__c1_(self):
self._seq([lambda: self._ch('u')])
def _hex_esc_(self):
self._push('hex_esc')
self._seq([lambda: self._ch('x'), lambda: self._bind(self._hex_, 'h1'),
lambda: self._bind(self._hex_, 'h2'),
lambda: self._succeed(self._xtou(self._get('h1') + self._get('h2')))])
self._pop('hex_esc')
def _unicode_esc_(self):
self._push('unicode_esc')
self._seq([lambda: self._ch('u'), lambda: self._bind(self._hex_, 'a'),
lambda: self._bind(self._hex_, 'b'),
lambda: self._bind(self._hex_, 'c'),
lambda: self._bind(self._hex_, 'd'),
lambda: self._succeed(self._xtou(self._get('a') + self._get('b') + self._get('c') + self._get('d')))])
self._pop('unicode_esc')
def _element_list_(self):
self._push('element_list')
self._seq([lambda: self._bind(self._value_, 'v'),
self._element_list__s1_, self._sp_, self._element_list__s3_,
lambda: self._succeed([self._get('v')] + self._get('vs'))])
self._pop('element_list')
def _element_list__s1_(self):
self._bind(lambda: self._star(self._element_list__s1_l_p_), 'vs')
def _element_list__s1_l_p_(self):
self._seq([self._sp_, lambda: self._ch(','), self._sp_, self._value_])
def _element_list__s3_(self):
self._opt(lambda: self._ch(','))
def _member_list_(self):
self._push('member_list')
self._seq([lambda: self._bind(self._member_, 'm'),
self._member_list__s1_, self._sp_, self._member_list__s3_,
lambda: self._succeed([self._get('m')] + self._get('ms'))])
self._pop('member_list')
def _member_list__s1_(self):
self._bind(lambda: self._star(self._member_list__s1_l_p_), 'ms')
def _member_list__s1_l_p_(self):
self._seq([self._sp_, lambda: self._ch(','), self._sp_, self._member_])
def _member_list__s3_(self):
self._opt(lambda: self._ch(','))
def _member_(self):
self._choose([self._member__c0_, self._member__c1_])
def _member__c0_(self):
self._push('member__c0')
self._seq([lambda: self._bind(self._string_, 'k'), self._sp_,
lambda: self._ch(':'), self._sp_,
lambda: self._bind(self._value_, 'v'),
lambda: self._succeed([self._get('k'), self._get('v')])])
self._pop('member__c0')
def _member__c1_(self):
self._push('member__c1')
self._seq([lambda: self._bind(self._ident_, 'k'), self._sp_,
lambda: self._ch(':'), self._sp_,
lambda: self._bind(self._value_, 'v'),
lambda: self._succeed([self._get('k'), self._get('v')])])
self._pop('member__c1')
def _ident_(self):
self._push('ident')
self._seq([lambda: self._bind(self._id_start_, 'hd'), self._ident__s1_,
lambda: self._succeed(self._join('', [self._get('hd')] + self._get('tl')))])
self._pop('ident')
def _ident__s1_(self):
self._bind(lambda: self._star(self._id_continue_), 'tl')
def _id_start_(self):
self._choose([self._ascii_id_start_, self._other_id_start_,
self._id_start__c2_])
def _id_start__c2_(self):
self._seq([self._bslash_, self._unicode_esc_])
def _ascii_id_start_(self):
self._choose([self._ascii_id_start__c0_, self._ascii_id_start__c1_,
self._ascii_id_start__c2_, self._ascii_id_start__c3_])
def _ascii_id_start__c0_(self):
self._range('a', 'z')
def _ascii_id_start__c1_(self):
self._range('A', 'Z')
def _ascii_id_start__c2_(self):
self._ch('$')
def _ascii_id_start__c3_(self):
self._ch('_')
def _other_id_start_(self):
self._choose([self._other_id_start__c0_, self._other_id_start__c1_,
self._other_id_start__c2_, self._other_id_start__c3_,
self._other_id_start__c4_, self._other_id_start__c5_])
def _other_id_start__c0_(self):
self._push('other_id_start__c0')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._other_id_start__c0__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('other_id_start__c0')
def _other_id_start__c0__s1_(self):
v = self._is_unicat(self._get('x'), 'Ll')
if v:
self._succeed(v)
else:
self._fail()
def _other_id_start__c1_(self):
self._push('other_id_start__c1')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._other_id_start__c1__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('other_id_start__c1')
def _other_id_start__c1__s1_(self):
v = self._is_unicat(self._get('x'), 'Lm')
if v:
self._succeed(v)
else:
self._fail()
def _other_id_start__c2_(self):
self._push('other_id_start__c2')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._other_id_start__c2__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('other_id_start__c2')
def _other_id_start__c2__s1_(self):
v = self._is_unicat(self._get('x'), 'Lo')
if v:
self._succeed(v)
else:
self._fail()
def _other_id_start__c3_(self):
self._push('other_id_start__c3')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._other_id_start__c3__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('other_id_start__c3')
def _other_id_start__c3__s1_(self):
v = self._is_unicat(self._get('x'), 'Lt')
if v:
self._succeed(v)
else:
self._fail()
def _other_id_start__c4_(self):
self._push('other_id_start__c4')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._other_id_start__c4__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('other_id_start__c4')
def _other_id_start__c4__s1_(self):
v = self._is_unicat(self._get('x'), 'Lu')
if v:
self._succeed(v)
else:
self._fail()
def _other_id_start__c5_(self):
self._push('other_id_start__c5')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._other_id_start__c5__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('other_id_start__c5')
def _other_id_start__c5__s1_(self):
v = self._is_unicat(self._get('x'), 'Nl')
if v:
self._succeed(v)
else:
self._fail()
def _id_continue_(self):
self._choose([self._ascii_id_start_, self._digit_,
self._other_id_start_, self._id_continue__c3_,
self._id_continue__c4_, self._id_continue__c5_,
self._id_continue__c6_, self._id_continue__c7_,
self._id_continue__c8_, self._id_continue__c9_])
def _id_continue__c3_(self):
self._push('id_continue__c3')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._id_continue__c3__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('id_continue__c3')
def _id_continue__c3__s1_(self):
v = self._is_unicat(self._get('x'), 'Mn')
if v:
self._succeed(v)
else:
self._fail()
def _id_continue__c4_(self):
self._push('id_continue__c4')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._id_continue__c4__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('id_continue__c4')
def _id_continue__c4__s1_(self):
v = self._is_unicat(self._get('x'), 'Mc')
if v:
self._succeed(v)
else:
self._fail()
def _id_continue__c5_(self):
self._push('id_continue__c5')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._id_continue__c5__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('id_continue__c5')
def _id_continue__c5__s1_(self):
v = self._is_unicat(self._get('x'), 'Nd')
if v:
self._succeed(v)
else:
self._fail()
def _id_continue__c6_(self):
self._push('id_continue__c6')
self._seq([lambda: self._bind(self._anything_, 'x'),
self._id_continue__c6__s1_,
lambda: self._succeed(self._get('x'))])
self._pop('id_continue__c6')
def _id_continue__c6__s1_(self):
v = self._is_unicat(self._get('x'), 'Pc')
if v:
self._succeed(v)
else:
self._fail()
def _id_continue__c7_(self):
self._seq([self._bslash_, self._unicode_esc_])
def _id_continue__c8_(self):
self._ch(u'\u200c')
def _id_continue__c9_(self):
self._ch(u'\u200d')
def _num_literal_(self):
self._choose([self._num_literal__c0_, self._num_literal__c1_,
self._hex_literal_, self._num_literal__c3_,
self._num_literal__c4_])
def _num_literal__c0_(self):
self._push('num_literal__c0')
self._seq([lambda: self._ch('-'),
lambda: self._bind(self._num_literal_, 'n'),
lambda: self._succeed('-' + self._get('n'))])
self._pop('num_literal__c0')
def _num_literal__c1_(self):
self._push('num_literal__c1')
self._seq([self._num_literal__c1__s0_,
lambda: self._bind(self._dec_literal_, 'd'),
lambda: self._not(self._id_start_),
lambda: self._succeed(self._get('d'))])
self._pop('num_literal__c1')
def _num_literal__c1__s0_(self):
self._opt(lambda: self._ch('+'))
def _num_literal__c3_(self):
self._str('Infinity')
def _num_literal__c4_(self):
self._str('NaN')
def _dec_literal_(self):
self._choose([self._dec_literal__c0_, self._dec_literal__c1_,
self._dec_literal__c2_, self._dec_literal__c3_,
self._dec_literal__c4_, self._dec_literal__c5_])
def _dec_literal__c0_(self):
self._push('dec_literal__c0')
self._seq([lambda: self._bind(self._dec_int_lit_, 'd'),
lambda: self._bind(self._frac_, 'f'),
lambda: self._bind(self._exp_, 'e'),
lambda: self._succeed(self._get('d') + self._get('f') + self._get('e'))])
self._pop('dec_literal__c0')
def _dec_literal__c1_(self):
self._push('dec_literal__c1')
self._seq([lambda: self._bind(self._dec_int_lit_, 'd'),
lambda: self._bind(self._frac_, 'f'),
lambda: self._succeed(self._get('d') + self._get('f'))])
self._pop('dec_literal__c1')
def _dec_literal__c2_(self):
self._push('dec_literal__c2')
self._seq([lambda: self._bind(self._dec_int_lit_, 'd'),
lambda: self._bind(self._exp_, 'e'),
lambda: self._succeed(self._get('d') + self._get('e'))])
self._pop('dec_literal__c2')
def _dec_literal__c3_(self):
self._push('dec_literal__c3')
self._seq([lambda: self._bind(self._dec_int_lit_, 'd'),
lambda: self._succeed(self._get('d'))])
self._pop('dec_literal__c3')
def _dec_literal__c4_(self):
self._push('dec_literal__c4')
self._seq([lambda: self._bind(self._frac_, 'f'),
lambda: self._bind(self._exp_, 'e'),
lambda: self._succeed(self._get('f') + self._get('e'))])
self._pop('dec_literal__c4')
def _dec_literal__c5_(self):
self._push('dec_literal__c5')
self._seq([lambda: self._bind(self._frac_, 'f'),
lambda: self._succeed(self._get('f'))])
self._pop('dec_literal__c5')
def _dec_int_lit_(self):
self._choose([self._dec_int_lit__c0_, self._dec_int_lit__c1_])
def _dec_int_lit__c0_(self):
self._seq([lambda: self._ch('0'), lambda: self._not(self._digit_),
lambda: self._succeed('0')])
def _dec_int_lit__c1_(self):
self._push('dec_int_lit__c1')
self._seq([lambda: self._bind(self._nonzerodigit_, 'd'),
self._dec_int_lit__c1__s1_,
lambda: self._succeed(self._get('d') + self._join('', self._get('ds')))])
self._pop('dec_int_lit__c1')
def _dec_int_lit__c1__s1_(self):
self._bind(lambda: self._star(self._digit_), 'ds')
def _digit_(self):
self._range('0', '9')
def _nonzerodigit_(self):
self._range('1', '9')
def _hex_literal_(self):
self._push('hex_literal')
self._seq([self._hex_literal__s0_, self._hex_literal__s1_,
lambda: self._succeed('0x' + self._join('', self._get('hs')))])
self._pop('hex_literal')
def _hex_literal__s0_(self):
self._choose([lambda: self._str('0x'), lambda: self._str('0X')])
def _hex_literal__s1_(self):
self._bind(lambda: self._plus(self._hex_), 'hs')
def _hex_(self):
self._choose([self._hex__c0_, self._hex__c1_, self._digit_])
def _hex__c0_(self):
self._range('a', 'f')
def _hex__c1_(self):
self._range('A', 'F')
def _frac_(self):
self._push('frac')
self._seq([lambda: self._ch('.'), self._frac__s1_,
lambda: self._succeed('.' + self._join('', self._get('ds')))])
self._pop('frac')
def _frac__s1_(self):
self._bind(lambda: self._star(self._digit_), 'ds')
def _exp_(self):
self._choose([self._exp__c0_, self._exp__c1_])
def _exp__c0_(self):
self._push('exp__c0')
self._seq([self._exp__c0__s0_,
lambda: self._bind(self._exp__c0__s1_l_, 's'),
self._exp__c0__s2_,
lambda: self._succeed('e' + self._get('s') + self._join('', self._get('ds')))])
self._pop('exp__c0')
def _exp__c0__s0_(self):
self._choose([lambda: self._ch('e'), lambda: self._ch('E')])
def _exp__c0__s1_l_(self):
self._choose([lambda: self._ch('+'), lambda: self._ch('-')])
def _exp__c0__s2_(self):
self._bind(lambda: self._star(self._digit_), 'ds')
def _exp__c1_(self):
self._push('exp__c1')
self._seq([self._exp__c1__s0_, self._exp__c1__s1_,
lambda: self._succeed('e' + self._join('', self._get('ds')))])
self._pop('exp__c1')
def _exp__c1__s0_(self):
self._choose([lambda: self._ch('e'), lambda: self._ch('E')])
def _exp__c1__s1_(self):
self._bind(lambda: self._star(self._digit_), 'ds')
def _anything_(self):
if self.pos < self.end:
self._succeed(self.msg[self.pos], self.pos + 1)
else:
self._fail()
def _end_(self):
if self.pos == self.end:
self._succeed(None)
else:
self._fail()

View File

@ -1,107 +0,0 @@
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed 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.
"""A tool to parse and pretty-print JSON5.
Usage:
$ echo '{foo:"bar"}' | python -m json5.tool
{
foo: 'bar',
}
$ echo '{foo:"bar"}' | python -m json5.tool --as-json
{
"foo": "bar"
}
"""
import sys
from . import arg_parser
from . import lib
from .host import Host
from .version import VERSION
def main(argv=None, host=None):
host = host or Host()
parser = arg_parser.ArgumentParser(host, prog='json5', desc=__doc__)
parser.add_argument('-c', metavar='STR', dest='cmd',
help='inline json5 string to read instead of '
'reading from a file')
parser.add_argument('--as-json', dest='as_json', action='store_const',
const=True, default=False,
help='output as JSON '
'(same as --quote-keys --no-trailing-commas)')
parser.add_argument('--indent', dest='indent', default=4,
help='amount to indent each line '
'(default is 4 spaces)')
parser.add_argument('--quote-keys', action='store_true', default=False,
help='quote all object keys')
parser.add_argument('--no-quote-keys', action='store_false',
dest='quote_keys',
help="don't quote object keys that are identifiers"
" (this is the default)")
parser.add_argument('--trailing-commas', action='store_true',
default=True,
help='add commas after the last item in multi-line '
'objects and arrays (this is the default)')
parser.add_argument('--no-trailing-commas', dest='trailing_commas',
action='store_false',
help='do not add commas after the last item in '
'multi-line lists and objects')
parser.add_argument('file', metavar='FILE', nargs='?', default='-',
help='optional file to read JSON5 document from; if '
'not specified or "-", will read from stdin '
'instead')
args = parser.parse_args(argv)
if parser.exit_status is not None:
return parser.exit_status
if args.version:
host.print_(VERSION)
return 0
if args.cmd:
inp = args.cmd
elif args.file == '-':
inp = host.stdin.read()
else:
inp = host.read_text_file(args.file)
if args.indent == 'None':
args.indent = None
else:
try:
args.indent = int(args.indent)
except ValueError:
pass
if args.as_json:
args.quote_keys = True
args.trailing_commas = False
obj = lib.loads(inp)
s = lib.dumps(obj,
indent=args.indent,
quote_keys=args.quote_keys,
trailing_commas=args.trailing_commas)
host.print_(s)
return 0
if __name__ == '__main__': # pragma: no cover
sys.exit(main())

View File

@ -1,15 +0,0 @@
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed 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.
VERSION = '0.9.6'

View File

@ -0,0 +1,20 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2021-2022 by shenjackyuanjie
# All rights reserved
# -------------------------------
import time
import ctypes
def run_print():
# 加载dll
# ctypes.cdll.LoadLibrary('stdio')
c_print = ctypes.cdll.LoadLibrary('./print.dll')
c_print.print_it(ctypes.c_wchar_p(u'12啊3'), ctypes.c_wchar_p('\n'))
c_print.print_it(ctypes.c_wchar_p(u'12啊3'), ctypes.c_wchar_p('\n'))
if __name__ == "__main__":
run_print()

View File

@ -0,0 +1,13 @@
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <stdlib.h>
#include <Python.h>
void print_it(wchar_t* out, wchar_t* end){
printf("%s%s", out, end);
};
static PyObject *print_it_py(PyObject *self, PyObject *args){
PyObject *back_obj = PyObject_Str(PyObject);
return back_obj;
};

Binary file not shown.

View File

@ -149,6 +149,7 @@ options = {
'debug_trace_depth': 1,
'debug_trace_flush': True,
'debug_win32': False,
'debug_input': False,
'debug_x11': False,
'shadow_window': True,
'vsync': None,
@ -178,6 +179,7 @@ _option_types = {
'debug_trace_depth': int,
'debug_trace_flush': bool,
'debug_win32': bool,
'debug_input': bool,
'debug_x11': bool,
'shadow_window': bool,
'vsync': bool,

View File

@ -245,7 +245,7 @@ class EventLoop(event.EventDispatcher):
self.clock.call_scheduled_functions(dt)
# Update timout
return self.clock.get_sleep_time(True)
return self.clock.get_sleep_time(False)
@property
def has_exit(self):

View File

@ -38,19 +38,19 @@
Measuring time
==============
The `tick` and `get_fps` functions can be used in conjunction to fulfil most
The `tick` and `get_frequency` functions can be used in conjunction to fulfil most
games' basic requirements::
from pyglet import clock
while True:
dt = clock.tick()
# ... update and render ...
print(f"FPS is {clock.get_fps()}")
print(f"FPS is {clock.get_frequency()}")
The ``dt`` value returned gives the number of seconds (as a float) since the
last "tick".
The `get_fps` function averages the framerate over a sliding window of
The `get_frequency` function averages the framerate over a sliding window of
approximately 1 second. (You can calculate the instantaneous framerate by
taking the reciprocal of ``dt``).
@ -176,7 +176,7 @@ class Clock:
self.next_ts = self.time()
self.last_ts = None
# Used by self.get_fps to show update frequency
# Used by self.get_frequency to show update frequency
self.times = _deque()
self.cumulative_time = 0
self.window_size = 60

View File

@ -1079,7 +1079,7 @@ class TabletCanvas(EventDispatcher):
:event:
"""
def on_motion(self, cursor, x, y, pressure, tilt_x, tilt_y):
def on_motion(self, cursor, x, y, pressure, tilt_x, tilt_y, buttons):
"""The cursor moved on the tablet surface.
If `pressure` is 0, then the cursor is actually hovering above the
@ -1099,6 +1099,9 @@ class TabletCanvas(EventDispatcher):
Currently undefined.
`tilt_y` : float
Currently undefined.
`buttons` : int
Button state may be provided if the platform supports it.
Supported on: Windows
:event:
"""

View File

@ -553,7 +553,8 @@ class EvdevControllerManager(ControllerManager, XlibSelectDevice):
self._controllers[name] = controller
if controller:
self.dispatch_event('on_connect', controller)
# Dispatch event in main thread:
pyglet.app.platform_event_loop.post_event(self, 'on_connect', controller)
def _make_device(self, name, count=1):
path = os.path.join('/dev/input', name)

View File

@ -34,12 +34,14 @@
# ----------------------------------------------------------------------------
import ctypes
from collections import defaultdict
import pyglet
from pyglet.input.base import DeviceOpenException
from pyglet.input.base import Tablet, TabletCanvas
from pyglet.libs.win32 import libwintab as wintab
from pyglet.util import debug_print
_debug = debug_print('debug_input')
lib = wintab.lib
@ -116,12 +118,19 @@ class WintabTablet(Tablet):
class WintabTabletCanvas(TabletCanvas):
override_keys = False
def __init__(self, device, window, msg_base=wintab.WT_DEFBASE):
super(WintabTabletCanvas, self).__init__(window)
self.device = device
self.msg_base = msg_base
# Get the extension masks available. Only need to do this once.
global _extension_masks
if not _extension_masks:
_extension_masks = get_extension_masks()
# Just use system context, for similarity w/ os x and xinput.
# WTI_DEFCONTEXT detaches mouse from tablet, which is nice, but not
# possible on os x afiak.
@ -132,11 +141,11 @@ class WintabTabletCanvas(TabletCanvas):
# If you change this, change definition of PACKET also.
context_info.lcPktData = (
wintab.PK_CHANGED | wintab.PK_CURSOR | wintab.PK_BUTTONS |
wintab.PK_X | wintab.PK_Y | wintab.PK_Z |
wintab.PK_NORMAL_PRESSURE | wintab.PK_TANGENT_PRESSURE |
wintab.PK_ORIENTATION)
context_info.lcPktMode = 0 # All absolute
wintab.PK_CHANGED | wintab.PK_CURSOR | wintab.PK_BUTTONS |
wintab.PK_X | wintab.PK_Y | wintab.PK_Z |
wintab.PK_NORMAL_PRESSURE | wintab.PK_TANGENT_PRESSURE |
wintab.PK_ORIENTATION) | _extension_masks
context_info.lcPktMode = 0 # All absolute (PACKETMODE)
self._context = lib.WTOpenW(window._hwnd, ctypes.byref(context_info), True)
if not self._context:
@ -145,10 +154,66 @@ class WintabTabletCanvas(TabletCanvas):
window._event_handlers[msg_base + wintab.WT_PACKET] = self._event_wt_packet
window._event_handlers[msg_base + wintab.WT_PROXIMITY] = self._event_wt_proximity
if _extension_masks:
window._event_handlers[msg_base + wintab.WT_PACKETEXT] = self._event_wt_packetext
self._current_cursor = None
self._pressure_scale = device.pressure_axis.get_scale()
self._pressure_bias = device.pressure_axis.get_bias()
self.express_keys = defaultdict(lambda: defaultdict(bool)) # [control_id][location_id]
self.express_key_ct = 0
self.touchrings = [] # Not currently implemented.
self.touchstrips = [] # Not currently implemented.
# Override test
for tablet_id in range(get_tablet_count()):
control_count = self.extension_get(wintab.WTX_EXPKEYS2, tablet_id, 0, 0,
wintab.TABLET_PROPERTY_CONTROLCOUNT)
self.express_key_ct = control_count
assert _debug(f"Controls Found: {control_count}")
if self.override_keys is True:
for control_id in range(control_count):
function_count = self.extension_get(wintab.WTX_EXPKEYS2, tablet_id, control_id, 0,
wintab.TABLET_PROPERTY_FUNCCOUNT)
for function_id in range(function_count):
self.extension_set(wintab.WTX_EXPKEYS2, tablet_id, control_id, function_id,
wintab.TABLET_PROPERTY_OVERRIDE, wintab.BOOL(True))
def extension_get(self, extension, tablet_id, control_id, function_id, property_id, value_type=wintab.UINT):
prop = wintab.EXTPROPERTY()
prop.version = 0
prop.tabletIndex = tablet_id
prop.controlIndex = control_id
prop.functionIndex = function_id
prop.propertyID = property_id
prop.reserved = 0
prop.dataSize = ctypes.sizeof(value_type)
success = lib.WTExtGet(self._context, extension, ctypes.byref(prop))
if success:
return ctypes.cast(prop.data, ctypes.POINTER(value_type)).contents.value
return 0
def extension_set(self, extension, tablet_id, control_id, function_id, property_id, value):
prop = wintab.EXTPROPERTY()
prop.version = 0
prop.tabletIndex = tablet_id
prop.controlIndex = control_id
prop.functionIndex = function_id
prop.propertyID = property_id
prop.reserved = 0
prop.dataSize = ctypes.sizeof(value)
prop.data[0] = value.value
success = lib.WTExtSet(self._context, extension, ctypes.byref(prop))
if success:
return True
return False
def close(self):
lib.WTClose(self._context)
self._context = None
@ -156,6 +221,9 @@ class WintabTabletCanvas(TabletCanvas):
del self.window._event_handlers[self.msg_base + wintab.WT_PACKET]
del self.window._event_handlers[self.msg_base + wintab.WT_PROXIMITY]
if _extension_masks:
del self.window._event_handlers[self.msg_base + wintab.WT_PACKETEXT]
def _set_current_cursor(self, cursor_type):
if self._current_cursor:
self.dispatch_event('on_leave', self._current_cursor)
@ -186,7 +254,25 @@ class WintabTabletCanvas(TabletCanvas):
if self._current_cursor is None:
self._set_current_cursor(packet.pkCursor)
self.dispatch_event('on_motion', self._current_cursor, x, y, pressure, 0., 0.)
self.dispatch_event('on_motion', self._current_cursor, x, y, pressure, 0., 0., packet.pkButtons)
@pyglet.window.win32.Win32EventHandler(0)
def _event_wt_packetext(self, msg, wParam, lParam):
packet = wintab.PACKETEXT()
if lib.WTPacket(lParam, wParam, ctypes.byref(packet)) == 0:
return
# Proper context exists in the packet, not the lParam.
if packet.pkBase.nContext == self._context:
if packet.pkExpKeys.nControl < self.express_key_ct:
current_state = self.express_keys[packet.pkExpKeys.nControl][packet.pkExpKeys.nLocation]
new_state = bool(packet.pkExpKeys.nState)
if current_state != new_state:
event_type = "on_express_key_press" if new_state else "on_express_key_release"
self.express_keys[packet.pkExpKeys.nControl][packet.pkExpKeys.nLocation] = new_state
self.dispatch_event(event_type, packet.pkExpKeys.nControl, packet.pkExpKeys.nLocation)
@pyglet.window.win32.Win32EventHandler(0)
def _event_wt_proximity(self, msg, wParam, lParam):
@ -205,6 +291,40 @@ class WintabTabletCanvas(TabletCanvas):
# can actually grab a cursor id.
self._current_cursor = None
def on_express_key_press(self, control_id: int, location_id: int):
"""An event called when an ExpressKey is pressed down.
:Parameters:
`control_id` : int
Zero-based index number given to the assigned key by the driver.
The same control_id may exist in multiple locations, which the location_id is used to differentiate.
`location_id: int
Zero-based index indicating side of tablet where control id was found.
Some tablets may have clusters of ExpressKey's on various areas of the tablet.
(0 = left, 1 = right, 2 = top, 3 = bottom, 4 = transducer).
:event:
"""
def on_express_key_release(self, control_id: int, location_id: int):
"""An event called when an ExpressKey is released.
:Parameters:
`control_id` : int
Zero-based index number given to the assigned key by the driver.
The same control_id may exist in multiple locations, which the location_id is used to differentiate.
`location_id: int
Zero-based index indicating side of tablet where control id was found.
Some tablets may have clusters of ExpressKey's on various areas of the tablet.
(0 = left, 1 = right, 2 = top, 3 = bottom, 4 = transducer).
:event:
"""
WintabTabletCanvas.register_event_type('on_express_key_press')
WintabTabletCanvas.register_event_type('on_express_key_release')
class WintabTabletCursor:
def __init__(self, device, index):
@ -244,11 +364,73 @@ def get_implementation_version():
return impl_version
def get_tablets(display=None):
# Require spec version 1.1 or greater
if get_spec_version() < 0x101:
return []
def extension_index(ext):
"""Check if a particular extension exists within the driver."""
exists = True
i = 0
index = 0xFFFFFFFF
while exists:
tag = wintab.UINT()
exists = lib.WTInfoW(wintab.WTI_EXTENSIONS + i, wintab.EXT_TAG, ctypes.byref(tag))
if tag.value == ext:
index = i
break
i += 1
if index != 0xFFFFFFFF:
return index
return None
def get_extension_masks():
"""Determine which extension support is available by getting the masks."""
masks = 0
tr_idx = extension_index(wintab.WTX_TOUCHRING)
if tr_idx is not None:
assert _debug("Touchring support found")
masks |= wtinfo_uint(wintab.WTI_EXTENSIONS + tr_idx, wintab.EXT_MASK)
else:
assert _debug("Touchring extension not found.")
ts_idx = extension_index(wintab.WTX_TOUCHSTRIP)
if ts_idx is not None:
assert _debug("Touchstrip support found.")
masks |= wtinfo_uint(wintab.WTI_EXTENSIONS + ts_idx, wintab.EXT_MASK)
else:
assert _debug("Touchstrip extension not found.")
expkeys_idx = extension_index(wintab.WTX_EXPKEYS2)
if expkeys_idx is not None:
assert _debug("ExpressKey support found.")
masks |= wtinfo_uint(wintab.WTI_EXTENSIONS + expkeys_idx, wintab.EXT_MASK)
else:
assert _debug("ExpressKey extension not found.")
return masks
def get_tablet_count():
"""Return just the number of current devices."""
spec_version = get_spec_version()
assert _debug(f"Wintab Version: {spec_version}")
if spec_version < 0x101:
return 0
n_devices = wtinfo_uint(wintab.WTI_INTERFACE, wintab.IFC_NDEVICES)
return n_devices
_extension_masks = None
def get_tablets(display=None):
# Require spec version 1.1 or greater
n_devices = get_tablet_count()
if not n_devices:
return []
devices = [WintabTablet(i) for i in range(n_devices)]
return devices

View File

@ -93,7 +93,7 @@ class XInputTabletCanvas(DeviceResponder, TabletCanvas):
x = e.x
y = self.window.height - e.y
pressure = e.axis_data[2] / float(cursor.max_pressure)
self.dispatch_event('on_motion', cursor, x, y, pressure, 0.0, 0.0)
self.dispatch_event('on_motion', cursor, x, y, pressure, 0.0, 0.0, 0.0)
def _proximity_in(self, e):
cursor = self._cursor_map.get(e.deviceid)

View File

@ -436,6 +436,7 @@ class XInputDeviceManager(EventDispatcher):
with self._dev_lock:
return [dev for dev in self.all_devices if dev.connected]
# Threaded method:
def _get_state(self):
xuser_max_count = set(range(XUSER_MAX_COUNT)) # {0, 1, 2, 3}
polling_rate = self._polling_rate
@ -457,7 +458,8 @@ class XInputDeviceManager(EventDispatcher):
# Found a new connection:
device.connected = True
self._connected_devices.add(i)
self.dispatch_event('on_connect', device)
# Dispatch event in main thread:
pyglet.app.platform_event_loop.post_event(self, 'on_connect', device)
elapsed = 0.0
@ -472,7 +474,8 @@ class XInputDeviceManager(EventDispatcher):
if device.connected:
device.connected = False
self._connected_devices.remove(i)
self.dispatch_event('on_disconnect', device)
# Dispatch event in main thread:
pyglet.app.platform_event_loop.post_event(self, 'on_disconnect', device)
continue
elif result == ERROR_SUCCESS and device.is_open:

View File

@ -35,6 +35,7 @@
import atexit
import struct
import warnings
import pyglet
from . import com

View File

@ -34,17 +34,13 @@
# ----------------------------------------------------------------------------
import ctypes
from ctypes.wintypes import HANDLE, BYTE, HWND, BOOL, UINT, LONG, WORD, DWORD, WCHAR, LPVOID
lib = ctypes.windll.wintab32
LONG = ctypes.c_long
BOOL = ctypes.c_int
UINT = ctypes.c_uint
WORD = ctypes.c_uint16
DWORD = ctypes.c_uint32
WCHAR = ctypes.c_wchar
FIX32 = DWORD
WTPKT = DWORD
HCTX = HANDLE # CONTEXT HANDLE
LCNAMELEN = 40
@ -119,6 +115,56 @@ class LOGCONTEXT(ctypes.Structure):
)
class TILT(ctypes.Structure): # 1.1
_fields_ = (
('tiltX', ctypes.c_int),
('tiltY', ctypes.c_int),
)
class EXTENSIONBASE(ctypes.Structure): # 1.4
_fields_ = (
('nContext', HCTX), # Specifies the Wintab context to which these properties apply.
('nStatus', UINT), # Status of setting/getting properties.
('nTime', DWORD), # Timestamp applied to property transaction.
('nSerialNumber', UINT), # Reserved - not use
)
class EXPKEYSDATA(ctypes.Structure): # 1.4
_fields_ = (
('nTablet', BYTE), # Tablet index where control is found.
('nControl', BYTE), # Zero-based control index.
('nLocation', BYTE), # Zero-based index indicating side of tablet where control found (0 = left, 1 = right).
('nReserved', BYTE), # Reserved - not used
('nState', DWORD) # Indicates Express Key button press (1 = pressed, 0 = released)
)
class SLIDERDATA(ctypes.Structure): # 1.4
_fields_ = (
('nTablet', BYTE), # Tablet index where control is found.
('nControl', BYTE), # Zero-based control index.
('nMode', BYTE), # Zero-based current active mode of control. Mode selected by control's toggle button.
('nReserved', BYTE), # Reserved - not used
('nPosition', DWORD) # An integer representing the position of the user's finger on the control.
# When there is no finger on the control, this value is negative.
)
class EXTPROPERTY(ctypes.Structure): # 1.4
_fields_ = (
('version', BYTE), # Structure version, 0 for now
('tabletIndex', BYTE), # 0-based index for tablet
('controlIndex', BYTE), # 0-based index for control
('functionIndex', BYTE), # 0-based index for control's sub-function
('propertyID', WORD), # property ID
('reserved', WORD), # DWORD-alignment filler
('dataSize', DWORD), # number of bytes in data[] buffer
('data', BYTE * 1), # raw data
)
# Custom packet format with fields
# PK_CHANGED
# PK_CURSOR
@ -143,6 +189,15 @@ class PACKET(ctypes.Structure):
)
class PACKETEXT(ctypes.Structure):
_fields_ = (
('pkBase', EXTENSIONBASE), # Extension control properties common to all control types.
('pkExpKeys', EXPKEYSDATA), # Extension data for one Express Key.
('pkTouchStrip', SLIDERDATA), # Extension data for one Touch Strip.
('pkTouchRing', SLIDERDATA) # Extension data for one Touch Ring.
)
PK_CONTEXT = 0x0001 # reporting context
PK_STATUS = 0x0002 # status bits
PK_TIME = 0x0004 # time stamp
@ -174,6 +229,7 @@ WT_CTXOVERLAP = 4
WT_PROXIMITY = 5
WT_INFOCHANGE = 6
WT_CSRCHANGE = 7
WT_PACKETEXT = 8
# system button assignment values
SBN_NONE = 0x00
@ -370,4 +426,47 @@ WTX_FKEYS = 1 # Function keys
WTX_TILT = 2 # Raw Cartesian tilt; 1.1
WTX_CSRMASK = 3 # select input by cursor type; 1.1
WTX_XBTNMASK = 4 # Extended button mask; 1.1
WTX_EXPKEYS = 5 # ExpressKeys; 1.3
WTX_EXPKEYS = 5 # ExpressKeys; 1.3 - DEPRECATED USE 2
WTX_TOUCHSTRIP = 6 # TouchStrips; 1.4
WTX_TOUCHRING = 7 # TouchRings; 1.4
WTX_EXPKEYS2 = 8 # ExpressKeys; 1.4
TABLET_PROPERTY_CONTROLCOUNT = 0 # UINT32: number of physical controls on tablet
TABLET_PROPERTY_FUNCCOUNT = 1 # UINT32: number of functions of control
TABLET_PROPERTY_AVAILABLE = 2 # BOOL: control/mode is available for override
TABLET_PROPERTY_MIN = 3 # UINT32: minimum value
TABLET_PROPERTY_MAX = 4 # UINT32: maximum value
TABLET_PROPERTY_OVERRIDE = 5 # BOOL: control is overridden
TABLET_PROPERTY_OVERRIDE_NAME = 6 # UTF-8: Displayable name when control is overridden
TABLET_PROPERTY_OVERRIDE_ICON = 7 # Image: Icon to show when control is overridden
TABLET_PROPERTY_ICON_WIDTH = 8 # UINT32: Pixel width of icon display
TABLET_PROPERTY_ICON_HEIGHT = 9 # UINT32: Pixel height of icon display
TABLET_PROPERTY_ICON_FORMAT = 10 # UINT32: UINT32: Pixel format of icon display (see TABLET_ICON_FMT_*)
TABLET_PROPERTY_LOCATION = 11 # UINT32: Physical location of control (see TABLET_LOC_*)
TABLET_LOC_LEFT = 0
TABLET_LOC_RIGHT = 1
TABLET_LOC_TOP = 2
TABLET_LOC_BOTTOM = 3
TABLET_LOC_TRANSDUCER = 4
lib.WTOpenW.restype = HCTX
lib.WTOpenW.argtypes = [HWND, ctypes.POINTER(LOGCONTEXT), BOOL]
lib.WTClose.restype = BOOL
lib.WTClose.argtypes = [HCTX]
lib.WTInfoW.restype = UINT
lib.WTInfoW.argtypes = [UINT, UINT, LPVOID]
lib.WTPacket.restype = BOOL
lib.WTPacket.argtypes = [HCTX, UINT, LPVOID]
lib.WTGetW.restype = BOOL
lib.WTGetW.argtypes = [HCTX, BOOL]
lib.WTExtGet.restype = BOOL
lib.WTExtGet.argtypes = [HCTX, UINT, LPVOID]
lib.WTExtSet.restype = BOOL
lib.WTExtSet.argtypes = [HCTX, UINT, LPVOID]

View File

@ -73,9 +73,11 @@ creating scrollable layouts.
.. versionadded:: 1.1
"""
import os.path
from os.path import dirname as _dirname
from os.path import splitext as _splitext
import pyglet
from pyglet.text import layout, document, caret
@ -130,7 +132,7 @@ def get_decoder(filename, mimetype=None):
:rtype: `DocumentDecoder`
"""
if mimetype is None:
_, ext = os.path.splitext(filename)
_, ext = _splitext(filename)
if ext.lower() in ('.htm', '.html', '.xhtml'):
mimetype = 'text/html'
else:
@ -176,7 +178,7 @@ def load(filename, file=None, mimetype=None):
if hasattr(file_contents, "decode"):
file_contents = file_contents.decode()
location = pyglet.resource.FileLocation(os.path.dirname(filename))
location = pyglet.resource.FileLocation(_dirname(filename))
return decoder.decode(file_contents, location)
@ -265,11 +267,7 @@ class DocumentLabel(layout.TextLayout):
Optional graphics group to use.
"""
super(DocumentLabel, self).__init__(document,
width=width, height=height,
multiline=multiline,
dpi=dpi, batch=batch, group=group)
super().__init__(document, width, height, multiline, dpi, batch, group)
self._x = x
self._y = y
self._anchor_x = anchor_x
@ -347,8 +345,7 @@ class DocumentLabel(layout.TextLayout):
@font_size.setter
def font_size(self, font_size):
self.document.set_style(0, len(self.document.text),
{'font_size': font_size})
self.document.set_style(0, len(self.document.text), {'font_size': font_size})
@property
def bold(self):
@ -360,8 +357,7 @@ class DocumentLabel(layout.TextLayout):
@bold.setter
def bold(self, bold):
self.document.set_style(0, len(self.document.text),
{'bold': bold})
self.document.set_style(0, len(self.document.text), {'bold': bold})
@property
def italic(self):
@ -373,8 +369,7 @@ class DocumentLabel(layout.TextLayout):
@italic.setter
def italic(self, italic):
self.document.set_style(0, len(self.document.text),
{'italic': italic})
self.document.set_style(0, len(self.document.text), {'italic': italic})
def get_style(self, name):
"""Get a document style value by name.
@ -404,6 +399,9 @@ class DocumentLabel(layout.TextLayout):
"""
self.document.set_style(0, len(self.document.text), {name: value})
def __del__(self):
self.delete()
class Label(DocumentLabel):
"""Plain text label.
@ -463,10 +461,8 @@ class Label(DocumentLabel):
Optional graphics group to use.
"""
document = decode_text(text)
super(Label, self).__init__(document, x, y, width, height,
anchor_x, anchor_y,
multiline, dpi, batch, group)
doc = decode_text(text)
super().__init__(doc, x, y, width, height, anchor_x, anchor_y, multiline, dpi, batch, group)
self.document.set_style(0, len(self.document.text), {
'font_name': font_name,
@ -525,10 +521,8 @@ class HTMLLabel(DocumentLabel):
"""
self._text = text
self._location = location
document = decode_html(text, location)
super(HTMLLabel, self).__init__(document, x, y, width, height,
anchor_x, anchor_y,
multiline, dpi, batch, group)
doc = decode_html(text, location)
super().__init__(doc, x, y, width, height, anchor_x, anchor_y, multiline, dpi, batch, group)
@property
def text(self):
@ -542,4 +536,3 @@ class HTMLLabel(DocumentLabel):
def text(self, text):
self._text = text
self.document = decode_html(text, self._location)

View File

@ -1851,7 +1851,7 @@ class FPSDisplay:
#: :type: float
update_period = 0.25
def __init__(self, window, color=(127, 127, 127, 127), samples=60):
def __init__(self, window, color=(127, 127, 127, 127), samples=240):
from time import time
from statistics import mean
from collections import deque
@ -1879,19 +1879,7 @@ class FPSDisplay:
if self._elapsed >= self.update_period:
self._elapsed = 0
self._set_fps_text(1 / self._mean(self._delta_times))
def _set_fps_text(self, fps):
"""Set the label text for the given FPS estimation.
Called by `update` every `update_period` seconds.
:Parameters:
`fps` : float
Estimated framerate of the window.
"""
self.label.text = '%.2f' % fps
self.label.text = f"{1 / self._mean(self._delta_times):.2f}"
def draw(self):
"""Draw the label."""

View File

@ -7,7 +7,6 @@
import os
import sys
import getopt
# input_args = sys.argv
@ -17,5 +16,5 @@ dllname = "./data_types.dll"
python_include_path = "C:/Users/shenjack.SHENJACK-5600X/AppData/Local/Programs/Python/Python38/include/"
os.system("gcc -I {} -shared {} -o {}".format(python_include_path, filename, dllname))
# gcc -I C:/Users/shenjack.SHENJACK-5600X/AppData/Local/Programs/Python/Python38/include/ -shared .\data_types.c -o .\data_types.dll
os.system("gcc -O3 -I {} -shared {} -o {}".format(python_include_path, filename, dllname))
# gcc -I C:/Users/shenjack.SHENJACK-5600X/AppData/Local/Programs/Python/Python38/include/ -shared .\data_types.c -o .\data_types.dll

1
viz_start.cmd Normal file
View File

@ -0,0 +1 @@
viztracer --output_file ./logs/viz_result.json --open --tracer_entries 10000000 DR.py