diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ae70c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__/ +.idea/ +dist/ +build/ +*egg-info/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9b18af1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7dbf039 --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# py-mock +![](https://shields.mitmproxy.org/pypi/v/py-mock.svg) +![](https://shields.mitmproxy.org/pypi/pyversions/py-mock.svg) + +```shell +pip install py-mock +``` +## py-mock 介绍 + +`py-mock` 移植了 [Mock.js](https://github.com/nuysoft/Mock +)、[better-mock](https://github.com/lavyun/better-mock)的功能到 Python,如果你熟悉 Mock +.js 的[模板语法](http://mockjs.com/examples.html), 那么在 Python 中也能轻而易举地 Mock + 出你想要的数据,`py-mock` `100%` 兼容 Mock.js。 + +## 一些说明 +实际上 `py-mock` 是借助了 `py_mini_racer` 来运行 Mock.js 中的 mock 函数,且仅移植了 Mock.mock + 方法,如果有问题可以在 github 上给我提 issue。 + +## 使用示例 + +```python +from pprint import pprint + +from pymock import Mock + +Mock = Mock() + +pprint(Mock.mock({ + 'list|1-10': [{ + 'id|+1': 1, + 'email': '@EMAIL' + }] +})) +``` +``` +{'list': [{'email': 'n.metv@mddwjpjxo.cf', 'id': 1}, + {'email': 'e.vseuqc@viiuxwde.biz', 'id': 2}, + {'email': 'v.eoje@mklgh.ba', 'id': 3}, + {'email': 'm.xobzjwhegf@hsclkd.uk', 'id': 4}]} +``` + +```python +pprint(Mock.mock(Mock.mock({ + 'number1|1-100.1-10': 1, + 'number2|123.1-10': 1, + 'number3|123.3': 1, + 'number4|123.10': 1.123 +}))) +``` +``` +{'number1': 56.5787, + 'number2': 123.14013355, + 'number3': 123.695, + 'number4': 123.1236478526} +``` + +```python +pprint(Mock.mock({ + 'regexp1': r'/[a-z][A-Z][0-9]/', + 'regexp2': r'/\w\W\s\S\d\D/', + 'regexp3': r'/\d{5,10}/' +})) +``` +``` +{'regexp1': 'mM8', 'regexp2': 'I;\rI5j', 'regexp3': '575824'} +``` +```python +pprint(Mock.mock({ + 'name': { + 'first': '@first', + 'middle': '@first', + 'last': '@last', + 'email': 'example\\@gmail.com', + 'full': '@first @middle @last' + } + } +)) +``` +``` +{'name': {'email': 'example@gmail.com', + 'first': 'Nancy', + 'full': 'Nancy Nancy Lee', + 'last': 'Lee', + 'middle': 'Nancy'}} +``` +```python +# JS object like string +pprint(Mock.mock( + """{ + name: { + first: '@cfirst', + middle: '@cfirst', + last: '@clast', + email: 'example\\@gmail.com', + full: '@first@middle@last' + } + }""" +)) +``` +``` +{'name': {'email': 'example@gmail.com', + 'first': '萧', + 'full': '萧白强', + 'last': '强', + 'middle': '白'}} +``` + +[更多示例](http://mockjs.com/examples.html) diff --git a/pymock/__init__.py b/pymock/__init__.py new file mode 100644 index 0000000..2235c14 --- /dev/null +++ b/pymock/__init__.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Author : mocobk +# @Email : mailmzb@qq.com +# @Time : 2020/8/18 19:12 +import codecs +import json +import json.encoder +import os +import re +import typing + +from py_mini_racer import py_mini_racer + +__JS_REGEX_PATTERN = re.compile(r'^/.*/$') +__JS_REGEX_ESCAPE_PATTERN = re.compile(r'^\\/.*\\/$') + + +def encode_regex_basestring_wrap(func): + def wrap(string: str): + if __JS_REGEX_PATTERN.match(string): + return string + else: + if __JS_REGEX_ESCAPE_PATTERN.match(string): + string = f'/{string[2:-2]}/' + return func(string) + + return wrap + + +json.encoder.encode_basestring = encode_regex_basestring_wrap(json.encoder.encode_basestring) +json.encoder.encode_basestring_ascii = encode_regex_basestring_wrap(json.encoder.encode_basestring_ascii) + + +class MiniRacer(py_mini_racer.MiniRacer): + def call(self, identifier, *args, **kwargs): + """ Call the named function with provided arguments + You can pass a custom JSON encoder by passing it in the encoder + keyword only argument. + """ + + encoder = kwargs.get('encoder', None) + timeout = kwargs.get('timeout', 0) + max_memory = kwargs.get('max_memory', 0) + + if isinstance(args[0], (dict, list)): + json_args = json.dumps(args, separators=(',', ':'), cls=encoder) + else: + json_args = f'[{args[0]}]' + js = "{identifier}.apply(this, {json_args})" + return self.eval(js.format(identifier=identifier, json_args=json_args), timeout, max_memory) + + +class Mock: + def __init__(self): + self.__code = codecs.open(os.path.join(os.path.dirname(__file__), 'js/mock.mini_racer.min.js'), + encoding='utf-8').read() + self.__ctx = MiniRacer() + self.__ctx.eval(self.__code) + + def mock(self, template: typing.Union[dict, list, str]) -> typing.Union[dict, list]: + """ + :param template: mock template + :return: dict, list + """ + return self.__ctx.call('Mock.mock', template) diff --git a/pymock/js/mock.mini_racer.min.js b/pymock/js/mock.mini_racer.min.js new file mode 100644 index 0000000..7ea8a66 --- /dev/null +++ b/pymock/js/mock.mini_racer.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Mock=t()}(this,function(){"use strict";var constant={GUID:1,RE_KEY:/(.+)\|(?:\+(\d+)|([\+\-]?\d+-?[\+\-]?\d*)?(?:\.(\d+-?\d*))?)/,RE_RANGE:/([\+\-]?\d+)-?([\+\-]?\d+)?/,RE_PLACEHOLDER:/\\*@([^@#%&()\?\s]+)(?:\((.*?)\))?/g},type=function(e){return isDef(e)?Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1].toLowerCase():String(e)},isDef=function(e){return null!=e},isNumber=function(e){return"number"===type(e)},isArray=function(e){return"array"===type(e)},isFunction=function(e){return"function"===type(e)},keys=function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t},values=function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(e[n]);return t},assert=function(e,t){if(!e)throw new Error("[better-mock] "+t)},__assign=function(){return(__assign=Object.assign||function(e){for(var t,n=1,r=arguments.length;n1/(e+t)*e?!n:n):.5<=Math.random()},bool=boolean,natural=function(e,t){return void 0===e&&(e=0),void 0===t&&(t=MAX_NATURE_NUMBER),e=parseInt(e.toString(),10),t=parseInt(t.toString(),10),Math.round(Math.random()*(t-e))+e},integer=function(e,t){return void 0===e&&(e=MIN_NATURE_NUMBER),void 0===t&&(t=MAX_NATURE_NUMBER),e=parseInt(e.toString(),10),t=parseInt(t.toString(),10),Math.round(Math.random()*(t-e))+e},int=integer,float=function(e,t,n,r){n=isDef(n)?n:0,n=Math.max(Math.min(n,17),0),r=isDef(r)?r:17,r=Math.max(Math.min(r,17),0);for(var a=integer(e,t)+".",i=0,c=natural(n,r);icn?(t=c.charAt(cn),cn++):(t=null,0===mn&&o(Ot)),null!==t?(on=n,null===(e=Ft(t))&&(cn=n),e):(cn=n,T)):(cn=n,T)}function R(){var e,t,n,r=cn;if(c.substr(cn,2)===qt?(e=qt,cn+=2):(e=null,0===mn&&o(Wt)),null!==e){if(t=[],Jt.test(c.charAt(cn))?(n=c.charAt(cn),cn++):(n=null,0===mn&&o(Kt)),null!==n)for(;null!==n;)t.push(n),Jt.test(c.charAt(cn))?(n=c.charAt(cn),cn++):(n=null,0===mn&&o(Kt));else t=T;r=null!==t?(on=r,null===(e=Qt(t))&&(cn=r),e):(cn=r,T)}else cn=r,r=T;return r}function E(){var e,t,n,r=cn;if(c.substr(cn,2)===Yt?(e=Yt,cn+=2):(e=null,0===mn&&o(Zt)),null!==e){if(t=[],Xt.test(c.charAt(cn))?(n=c.charAt(cn),cn++):(n=null,0===mn&&o(Vt)),null!==n)for(;null!==n;)t.push(n),Xt.test(c.charAt(cn))?(n=c.charAt(cn),cn++):(n=null,0===mn&&o(Vt));else t=T;r=null!==t?(on=r,null===(e=en(t))&&(cn=r),e):(cn=r,T)}else cn=r,r=T;return r}function M(){var e,t,n,r=cn;if(c.substr(cn,2)===tn?(e=tn,cn+=2):(e=null,0===mn&&o(nn)),null!==e){if(t=[],Xt.test(c.charAt(cn))?(n=c.charAt(cn),cn++):(n=null,0===mn&&o(Vt)),null!==n)for(;null!==n;)t.push(n),Xt.test(c.charAt(cn))?(n=c.charAt(cn),cn++):(n=null,0===mn&&o(Vt));else t=T;r=null!==t?(on=r,null===(e=rn(t))&&(cn=r),e):(cn=r,T)}else cn=r,r=T;return r}function P(){var e,t=cn;return c.substr(cn,2)===qt?(e=qt,cn+=2):(e=null,0===mn&&o(Wt)),null!==e&&(on=t,e=an()),t=(null===e&&(cn=t),e)}function S(){var e,t,n=cn;return 92===c.charCodeAt(cn)?(e=Ht,cn++):(e=null,0===mn&&o(Ut)),n=null!==e?(c.length>cn?(t=c.charAt(cn),cn++):(t=null,0===mn&&o(Ot)),null!==t?(on=n,null===(e=Be(t))&&(cn=n),e):(cn=n,T)):(cn=n,T)}var t,n=1