def distribute_package(root, version=None, repository=None, *, upload=True): """ 发布包的工具函数 :param root: 项目的根目录,例如 'D:/slns/pyxllib' 根目录下有对应的 setup.py 等文件 :param repository: 比如我配置了 [xlpr],就可以传入 'xlpr' """ from pyxllib.file.specialist import File, Dir # 1 切换工作目录 os.chdir(str(root)) # 2 改版本号 if version: f = File('setup.py', root) s = re.sub(r"(version\s*=\s*)(['\"])(.+?)(\2)", fr'\1\g<2>{version}\4', f.read()) f.write(s) # 3 打包 subprocess.run('python setup.py sdist') # 4 上传 if upload: # 上传 cmd = 'twine upload dist/*' if repository: cmd += f' -r {repository}' subprocess.run(cmd) # 删除打包生成的中间文件 Dir('dist').delete() Dir('build').delete()
def browser(_self, opt='html'): if opt == 'html': data = _self.get_text('html') # html、xhtml 可以转网页,虽然排版相对来说还是会乱一点 data = ''.join(data) etag = get_etag(data) f = File(etag, Dir.TEMP, suffix='.html') f.write(data) browser(f) else: raise ValueError
def to_image(_self, outfile, *, scale=1, if_exists=None): """ 转成为文件 """ f = File(outfile) suffix = f.suffix.lower() if suffix == '.svg': content = _self.get_svg_image() f.write(content, if_exists=if_exists) else: im = _self.get_cv_image(scale) xlcv.write(im, if_exists=if_exists)
def bcompare(self): """ 可以通过subinput设置文件后缀类型 """ from pyxllib.debug.specialist import bcompare suffix = self.cmds.get('subinput', None) try: f1 = File('left', Dir.TEMP, suffix=suffix) f2 = File('right', Dir.TEMP, suffix=suffix) except OSError: # 忽略错误的扩展名 f1 = File('left', Dir.TEMP) f2 = File('right', Dir.TEMP) f1.write(suffix) f2.write(self.cmds['ClipText']) bcompare(f1, f2, wait=False)
def write(self, loc): f = File(loc, self.root, suffix='.json') imfile = self.imfiles[loc] lmdict = LabelmeDict.gen_data(imfile) for label, ann in self.data[loc].items(): a = ann.copy() DictTool.isub(a, ['img']) shape = LabelmeDict.gen_shape(json.dumps(a, ensure_ascii=False), a['points'], a['shape_type'], group_id=a['group_id'], flags=a['flags']) lmdict['shapes'].append(shape) f.write(lmdict, indent=2)
def write(self, relpath, **kwargs): """ :param relpath: 必须是斜杠表示的相对路径 'a/1.txt'、'b/2.json' """ data = self.rp2data[relpath] file = File(relpath, self.root) if file: # 如果文件存在,要遵循原有的编码规则 with open(str(file), 'rb') as f: bstr = f.read() encoding = get_encoding(bstr) kwargs['encoding'] = encoding kwargs['if_exists'] = 'replace' file.write(data, **kwargs) else: # 否则直接写入 file.write(data, **kwargs)
def browser(self, opt='pdf'): if opt == 'pdf': f = self.src_file browser(self.src_file) elif opt == 'html': ls = [] for i in range(self.page_count): page = self.load_page(i) ls.append(page.get_text('html')) data = '\n'.join(ls) etag = get_etag(data) f = File(etag, Dir.TEMP, suffix='.html') f.write(data) browser(f) else: raise ValueError(f'{opt}') return f
class PairContent: """ 配对文本类,主要用于bc差异比较 """ def __init__(self, left_file_name=None, right_file_name=None): self.left_file = File( left_file_name) if left_file_name else left_file_name self.right_file = File( right_file_name) if right_file_name else right_file_name self.left, self.right = [], [] def add(self, lt, rt=None): """ rt不加,默认本轮内容是同lt """ lt = str(lt) self.left.append(lt) rt = lt if rt is None else str(rt) self.right.append(rt) def bcompare(self, **kwargs): left, right = '\n'.join(self.left), '\n'.join(self.right) if self.left_file is not None: left = self.left_file.write(left) if self.right_file is not None: right = self.right_file.write(right) bcompare(left, right, **kwargs)
def browser_jsons_kv(fd, files='**/*.json', encoding=None, max_items=10, max_value_length=100): """ demo_keyvaluescounter,查看目录下json数据的键值对信息 :param fd: 目录 :param files: 匹配的文件格式 :param encoding: 文件编码 :param max_items: 项目显示上限,有些数据项目太多了,要精简下 设为假值则不设上限 :param max_value_length: 添加的值,进行截断,防止有些值太长 :return: """ kvc = KeyValuesCounter() d = Dir(fd) for p in d.select_files(files): # print(p) data = p.read(encoding=encoding, mode='.json') kvc.add(data, max_value_length=max_value_length) p = File(r'demo_keyvaluescounter.html', Dir.TEMP) p.write(kvc.to_html_table(max_items=max_items), if_exists='replace') browser(p.to_str())
def modify_file(file, func, *, outfile=None, file_mode=None, debug=0): """ 对单个文件就行优化的功能函数 这样一些底层函数功能可以写成数据级的接口,然后由这个函数负责文件的读写操作,并且支持debug比较前后内容差异 :param outfile: 默认是对file原地操作,如果使用该参数,则将处理后的内容写入outfile文件 :param file_mode: 指定文件读取类型格式,例如'.json'是json文件,读取为字典 :param debug: 这个功能可以对照refine分级轮理解 无outfile参数时,原地操作 0,【直接原地操作】关闭调试,直接运行 (根据outfile=None选择原地操作,或者指定生成新文件) 1,【进行审核】打开BC比较差异,左边原始内容,右边参考内容 (打开前后差异内容对比) -1,【先斩后奏】介于完全不调试和全部人工检查之间,特殊的差异比较。左边放原始文件修改后的结果,右边对照原内容。 有outfile参数时 0 | False,直接生成目标文件,如果outfile已存在会被覆盖 1 | True,直接生成目标文件,但是会弹出bc比较前后内容差异 (相同内容不会弹出) """ infile = File(file) enc = get_encoding(infile.read(mode='b')) data = infile.read(mode=file_mode, encoding=enc) origin_content = str(data) new_data = func(data) isdiff = origin_content != str(new_data) if outfile is None: # 原地操作 if isdiff: # 内容不同才会有相关debug功能,否则静默跳过就好 if debug == 0: infile.write(new_data, mode=file_mode) # 直接处理 elif debug == 1: temp_file = File('refine_content', Dir.TEMP, suffix=infile.suffix).write(new_data) bcompare(infile, temp_file) # 使用beyond compare软件打开对比查看 elif debug == -1: temp_file = File('origin_content', Dir.TEMP, suffix=infile.suffix) infile.copy(temp_file) infile.write(new_data, mode=file_mode, encoding=enc) # 把原文件内容替换了 bcompare(infile, temp_file) # 然后显示与旧内容进行对比 else: raise ValueError(f'{debug}') else: outfile = File(outfile) outfile.write(new_data, mode=file_mode, encoding=enc) # 直接处理 if debug and isdiff: bcompare(infile, outfile) return isdiff
def from_content(cls, html_content, title='temphtml', *, encoding=None, number=True, text_catalogue=True): """ :param html_content: 原始网页的完整内容 :param title: 页面标题,默认会先找head/title,如果没有,则取一个随机名称(TODO 未实装,目前固定名称) :param encoding: 保存的几个文件编码,默认是utf8,但windows平台有些特殊场合也可能要存储gbk :param number: 是否对每节启用自动编号的css 算法基本原理:读取原网页,找出所有h标签,并增设a锚点 另外生成一个导航html文件 然后再生成一个主文件,让用户通过主文件来浏览页面 # 读取csdn博客并展示目录 (不过因为这个存在跳级,效果不是那么好) >> file = 自动制作网页标题的导航栏(requests.get(r'https://blog.csdn.net/code4101/article/details/83009000').content.decode('utf8')) >> browser(str(file)) http://i2.tiimg.com/582188/64f40d235705de69.png """ from humanfriendly import format_size # 1 对原html,设置锚点,生成一个新的文件f2 cnt = 0 # 这个refs是可以用py算法生成的,目前是存储在github上引用 refs = [ '<html><head>', '<link rel=Stylesheet type="text/css" media=all ' f'href="https://code4101.github.io/css/navigation{int(number)}.css">', '</head><body>' ] f2 = File(title + '_content', Dir.TEMP, suffix='.html') def func(m): nonlocal cnt cnt += 1 name, content = m.group('name'), m.group('inner') content = BeautifulSoup(content, 'lxml').get_text() # 要写<h><a></a></h>,不能写<a><h></h></a>,否则css中设置的计数器重置不会起作用 refs.append( f'<{name}><a href="{f2}#navigation{cnt}" target="showframe">{content}</a></{name}>' ) return f'<a name="navigation{cnt}"/>' + m.group() html_content = re.sub( r'<(?P<name>h\d+)(?:>|\s.*?>)(?P<body>\s*(?P<inner>.*?)\s*)</\1>', func, html_content, flags=re.DOTALL) f2 = f2.write(html_content, encoding=encoding, if_exists='replace') # 2 f1除了导航栏,可以多附带一些有用的参考信息 # 2.1 前文的refs已经存储了超链接的导航 # 2.2 文本版的目录 bs = BeautifulSoup(html_content, 'lxml') text = bs.get_text() if text_catalogue: # 目录 refs.append(f'<br/>【文本版的目录】') catalogue = bs.get_catalogue(indent='\t', start_level=-1, jump=True, size=True) refs.append(f'<pre>{catalogue}</pre>') # 全文长度 n = strwidth(text) refs.append('<br/>【Total Bytes】' + format_size(n)) # 2.3 文中使用的高频词 # 英文可以直接按空格切开统计,区分大小写 text2 = re.sub(grp_chinese_char(), '', text) # 删除中文,先不做中文的功能~~ text2 = re.sub(r'[,\.,。\(\)();;??"]', ' ', text2) # 标点符号按空格处理 words = Counter(text2.split()) msg = '\n'.join([(x[0] if x[1] == 1 else f'{x[0]},{x[1]}') for x in words.most_common()]) msg += f'<br/>共{len(words)}个词汇,用词数{sum(words.values())}。' refs.append(f'<br/>【词汇表】<pre>{msg}</pre>') # 2.5 收尾,写入f1 refs.append('</body>\n</html>') f1 = File(title + '_catalogue', Dir.TEMP, suffix='.html').write('\n'.join(refs), encoding=encoding, if_exists='replace') # 3 生成主页 f0 main_content = f"""<html> <frameset cols="20%,80%"> <frame src="{f1}"> <frame src="{f2}" name="showframe"> </frameset></html>""" f0 = File(title + '_index', Dir.TEMP, suffix='.html').write(main_content, encoding=encoding, if_exists='replace') return f0
def write(im, file, if_exists='replace'): if not isinstance(file, File): file = File(file) data = cv2.imencode(ext=file.suffix, img=im)[1] return file.write(data.tobytes(), if_exists=if_exists)
def showdir(c, *, to_html=None, printf=True, width=200): """查看类信息 会罗列出类c的所有成员方法、成员变量,并生成一个html文 查阅一个对象的成员变量及成员方法 为了兼容linux输出df时也能对齐,有几个中文域宽处理相关的函数 :param c: 要处理的对象 :param to_html: win32上默认True,用chrome、explorer打开 linux上默认False,直接输出到控制台 :param printf: 默认是True,会输出到浏览器或控制条 设为False则不输出 :param width: 属性列显示值的上限字符数 """ # 1 输出类表头 from humanfriendly import format_size res = [] object_name = func_input_message(2)['argnames'][0] if to_html is None: to_html = sys.platform == 'win32' newline = '<br/>' if to_html else '\n' t = f'==== 对象名称:{object_name},类继承关系:{inspect.getmro(type(c))},' \ + f'内存消耗:{format_size(sys.getsizeof(c), binary=True)}' \ + f'(递归子类总大小:{format_size(getasizeof(c), binary=True)}) ====' if to_html: res.append('<p>') t = html.escape(t) + '</p>' res.append(t + newline) # 2 html的样式精调 def df2str(df): if to_html: df = df.applymap(str) # 不转成文本经常有些特殊函数会报错 df.index += 1 # 编号从1开始 # pd.options.display.max_colwidth = -1 # 如果临时需要显示完整内容 t = df.to_html() table = BeautifulSoup(t, 'lxml') table.thead.tr['bgcolor'] = 'LightSkyBlue' # 设置表头颜色 # 根据pycharm的规则,命名应该是成员变量Field,成员方法Member ch = 'F' if '成员变量' in table.tr.contents[3].string else 'M' table.thead.tr.th.string = f'编号{ch}{len(df)}' t = table.prettify() else: # 直接转文本,遇到中文是会对不齐的,但是showdir主要用途本来就是在浏览器看的,这里就不做调整了 t = dataframe_str(df) return t # 3 添加成员变量和成员函数 # 成员变量 members = getmembers(c) methods = filter(lambda m: not callable(getattr(c, m[0])), members) ls = [] for ele in methods: k, v = ele if k.endswith(r'________'): # 这个名称的变量是我代码里的特殊标记,不显示 continue attr = getattr(c, k) if isinstance(attr, enum.IntFlag): # 对re.RegexFlag等枚举类输出整数值 v = typename(attr) + ',' + str(int(attr)) + ',' + str(v) else: try: text = str(v) except: text = '取不到str值' v = typename(attr) + ',' + shorten(text, width=width) ls.append([k, v]) df = pd.DataFrame.from_records(ls, columns=['成员变量', '描述']) res.append(df2str(df) + newline) # 成员函数 methods = filter(lambda m: callable(getattr(c, m[0])), members) df = pd.DataFrame.from_records(methods, columns=['成员函数', '描述']) res.append(df2str(df) + newline) res = newline.join(res) # 4 使用chrome.exe浏览或输出到控制台 # 这里底层可以封装一个chrome函数来调用,但是这个chrome需要依赖太多功能,故这里暂时手动简单调用 if to_html: if isinstance(to_html, str): # 如果是字符串,则认为是指定了输出文件的路径 f = File(to_html, suffix='.html') else: f = File(object_name, Dir.TEMP, suffix='.html') filename = f.write(ensure_gbk(res), if_exists='replace').to_str() browser(filename) else: # linux环境直接输出表格 print(res) return res