Win32 Python support done, remove duplicate files #1289

This commit is contained in:
guoling
2024-06-28 10:58:48 +08:00
parent 25f2d39a3c
commit f2442b9a0a
7 changed files with 1 additions and 986 deletions
+1 -4
View File
@@ -1,6 +1,3 @@
[submodule "POSIX/Python/pybind11"]
[submodule "Python/pybind11"]
path = Python/pybind11
url = https://github.com/pybind/pybind11.git
[submodule "Win32/Python/pybind11"]
path = Win32/Python/pybind11
url = https://github.com/pybind/pybind11.git
-56
View File
@@ -1,56 +0,0 @@
#
# Tencent is pleased to support the open source community by making
# MMKV available.
#
# Copyright (C) 2020 THL A29 Limited, a Tencent company.
# All rights reserved.
#
# Licensed under the BSD 3-Clause License (the "License"); you may not use
# this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://opensource.org/licenses/BSD-3-Clause
#
# 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.
#
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.0)
project(mmkv_python)
IF(APPLE)
add_compile_definitions(FORCE_POSIX)
ENDIF()
add_subdirectory(../../Core Core)
add_subdirectory(pybind11)
pybind11_add_module( # Sets the name of the library.
mmkv
# Sets the library as a python module library.
MODULE
# Provides a relative path to your source file(s).
libmmkv_python.cpp
)
target_include_directories(mmkv PUBLIC
${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(mmkv PROPERTIES
CXX_STANDARD 17
CXX_EXTENSIONS OFF
)
target_link_libraries( # Specifies the target library.
mmkv PRIVATE
core
)
-253
View File
@@ -1,253 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import mmkv
import time
import tempfile
def functional_test(mmap_id, decode_only):
# pass MMKVMode.MultiProcess to get a multi-process instance
# kv = mmkv.MMKV('test_python', mmkv.MMKVMode.MultiProcess)
kv = mmkv.MMKV(mmap_id)
if not decode_only:
kv.set(True, 'bool')
print('bool = ', kv.getBool('bool'))
if not decode_only:
kv.set(-1 * (2 ** 31), 'int32')
print('int32 = ', kv.getInt('int32'))
if not decode_only:
kv.set((2 ** 32) - 1, 'uint32')
print('uint32 = ', kv.getUInt('uint32'))
if not decode_only:
kv.set(2 ** 63, 'int64')
print('int64 = ', kv.getLongInt('int64'))
if not decode_only:
kv.set((2 ** 64) - 1, 'uint64')
print('uint64 = ', kv.getLongUInt('uint64'))
if not decode_only:
kv.set(3.1415926, 'float')
print('float = ', kv.getFloat('float'))
if not decode_only:
kv.set('Hello world, MMKV for Python!', 'string')
print('string = ', kv.getString('string'))
if not decode_only:
ls = range(0, 10)
kv.set(bytes(ls), 'bytes')
b = kv.getBytes('bytes')
print('raw bytes = ', b)
if sys.version_info >= (3, 0):
print('decoded bytes = ', list(b))
if not decode_only:
print('keys before remove:', sorted(kv.keys()))
kv.remove('bool')
print('"bool" exist after remove: ', ('bool' in kv))
kv.remove(['int32', 'float'])
print('keys after remove:', sorted(kv.keys()))
else:
print('keys:', sorted(kv.keys()))
def test_backup():
temp_dir = tempfile.gettempdir()
root_dir = temp_dir + '/mmkv_backup'
mmap_id = "test_python"
ret = mmkv.MMKV.backupOneToDirectory(mmap_id, root_dir)
print("backup one return: ", ret)
kv = mmkv.MMKV("test/Encrypt", mmkv.MMKVMode.SingleProcess, "cryptKey")
kv.remove("test_restore_key")
count = mmkv.MMKV.backupAllToDirectory(root_dir)
print("backup all count: ", count)
# just for testing
def utf8len(s):
return len(s.encode('utf-8'))
def test_expected_capacity():
key = "key0"
value = "🏊🏻®4️⃣🐅_"
dataLen = 10000
for i in range(dataLen):
value += "0"
print("value size =", utf8len(value))
expectedSize = utf8len(key) + utf8len(value)
# if we know exactly the sizes of key and value, set expectedCapacity for performance improvement
kv = mmkv.MMKV("mmkv_capacity0", mmkv.MMKVMode.SingleProcess, "", "", expectedSize)
# 0 times expand
kv.set(value, key)
print("data size from MMKV =", len(kv.getString(key)))
countTick = 10
expectedSize *= countTick
kv = mmkv.MMKV("mmkv_capacity1", mmkv.MMKVMode.SingleProcess, "", "", expectedSize)
for i in range(countTick):
key1 = "key" + str(i)
# 0 times expand
kv.set(value, key1)
def test_restore():
temp_dir = tempfile.gettempdir()
root_dir = temp_dir + '/mmkv_backup'
mmap_id = "test/Encrypt"
aes_key = "cryptKey"
a_kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, aes_key)
a_kv.set("string value before restore", "test_restore_key")
print("before restore [", a_kv.mmapID(), "] allKeys: ", a_kv.keys())
ret = mmkv.MMKV.restoreOneFromDirectory(mmap_id, root_dir)
print("restore one return: ", ret)
if ret:
print("after restore [", a_kv.mmapID(), "] allKeys: ", a_kv.keys())
count = mmkv.MMKV.restoreAllFromDirectory(root_dir)
print("restore all count: ", count)
if count > 0:
backup_mmkv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, aes_key)
print("check on restore [", backup_mmkv.mmapID(), "] allKeys: ", backup_mmkv.keys())
backup_mmkv = mmkv.MMKV("test_python")
print("check on restore [", backup_mmkv.mmapID(), "] allKeys: ", backup_mmkv.keys())
def test_auto_expire():
kv = mmkv.MMKV("test_auto_expire")
kv.clearAll(True)
kv.disableAutoKeyExpire()
kv.set(True, "auto_expire_key_1")
kv.enableAutoKeyExpire(1)
kv.set("never_expire_value_1", "never_expire_key_1", 0)
time.sleep(2)
print("contains auto_expire_key_1:", "auto_expire_key_1" in kv)
print("contains never_expire_key_1:", "never_expire_key_1" in kv)
kv.remove("never_expire_key_1")
kv.enableAutoKeyExpire(0)
kv.set("never_expire_value_1", "never_expire_key_1")
kv.set(True, "auto_expire_key_1", 1)
time.sleep(2)
print("contains never_expire_key_1:", "never_expire_key_1" in kv)
print("contains auto_expire_key_1:", "auto_expire_key_1" in kv)
print("count filter expire key:", kv.count(True))
print("all non expire keys:", kv.keys(True))
def test_compare_before_set():
kv = mmkv.MMKV("testCompareBeforeSet")
kv.enableCompareBeforeSet()
kv.set("extraValue", "extraKey")
key = "bool"
kv.set(True, key)
print("testCompareBeforeSet: bool value = ", kv.getBool(key))
actualSize1 = kv.actualSize()
print("testCompareBeforeSet: actualSize = ", actualSize1)
print("testCompareBeforeSet: bool value = ", kv.getBool(key))
kv.set(True, key)
actualSize2 = kv.actualSize()
print("testCompareBeforeSet: actualSize2 = ", actualSize2)
if actualSize1 != actualSize2:
raise("size not match")
kv.set(False, key)
print("testCompareBeforeSet: bool value = ", kv.getBool(key))
if kv.getBool(key):
print("value not update")
s1 = "🏊🏻®hhh4️⃣🐅_yyy"
s2 = "0aA🏊🏻®hhh4️⃣🐅_zzz"
key = "string"
kv.set(s1, key)
resultString = kv.getString(key)
print("testCompareBeforeSet: string = ", resultString)
actualSize1 = kv.actualSize()
print("testCompareBeforeSet: actualSize = ", actualSize1)
resultString = kv.getString(key)
print("testCompareBeforeSet: string = ", resultString)
kv.set(s1, key)
actualSize2 = kv.actualSize()
if actualSize1 != actualSize2:
print("size not match")
kv.set(s2, key)
resultString = kv.getString(key)
print("testCompareBeforeSet: string = ", resultString)
if resultString != s2:
print("value not update")
kv.disableCompareBeforeSet()
def test_remove_storage():
kv = mmkv.MMKV("test_remove", mmkv.MMKVMode.MultiProcess)
kv.set(True, "bool")
mmkv.MMKV.removeStorage("test_remove")
kv = mmkv.MMKV("test_remove", mmkv.MMKVMode.MultiProcess)
if kv.count() != 0:
print("storage not successfully remove")
temp_dir = tempfile.gettempdir()
rootDir = temp_dir + "/dev/mmkv_sg"
kv = mmkv.MMKV("test_remove/sg", rootDir = rootDir)
kv.set(True, "bool")
mmkv.MMKV.removeStorage("test_remove/sg")
kv = mmkv.MMKV("test_remove/sg", rootDir = rootDir)
if kv.count() != 0:
print("storage not successfully remove")
def logger(log_level, file, line, function, message):
level = {mmkv.MMKVLogLevel.NoLog: 'N',
mmkv.MMKVLogLevel.Debug: 'D',
mmkv.MMKVLogLevel.Info: 'I',
mmkv.MMKVLogLevel.Warning: 'W',
mmkv.MMKVLogLevel.Error: 'E'}
print('r-[{0}] <{1}:{2}:{3}> {4}'.format(level[log_level], file, line, function, message))
def error_handler(mmap_id, error_type):
print('[{}] has error {}'.format(mmap_id, error_type))
return mmkv.MMKVErrorType.OnErrorRecover
def content_change_handler(mmap_id):
print("[%s]'s content has been changed by other process" % mmap_id)
if __name__ == '__main__':
temp_dir = tempfile.gettempdir()
temp_dir = temp_dir + '/mmkv'
mmkv.MMKV.initializeMMKV(temp_dir)
test_expected_capacity()
functional_test('test_python', False)
test_backup()
test_restore()
test_auto_expire()
test_compare_before_set()
test_remove_storage()
mmkv.MMKV.onExit()
-387
View File
@@ -1,387 +0,0 @@
/*
* Tencent is pleased to support the open source community by making
* MMKV available.
*
* Copyright (C) 2020 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of
* the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.
*/
#include "MMKV.h"
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <basetsd.h>
using namespace mmkv;
using namespace std;
namespace py = pybind11;
typedef SSIZE_T ssize_t;
static MMBuffer pyBytes2MMBuffer(const py::bytes &bytes) {
char *buffer = nullptr;
ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(bytes.ptr(), &buffer, &length) == 0) {
return {buffer, static_cast<size_t>(length), MMBufferNoCopy};
}
return MMBuffer(0);
}
static function<void(MMKVLogLevel level, const char *file, int line, const char *function, const string &message)>
g_logHandler = nullptr;
static void MyLogHandler(MMKVLogLevel level, const char *file, int line, const char *function, const string &message) {
if (g_logHandler) {
g_logHandler(level, file, line, function, message);
}
}
static function<MMKVRecoverStrategic(const string &mmapID, MMKVErrorType errorType)> g_errorHandler = nullptr;
static MMKVRecoverStrategic MyErrorHandler(const string &mmapID, MMKVErrorType errorType) {
if (g_errorHandler) {
return g_errorHandler(mmapID, errorType);
}
return OnErrorDiscard;
}
static function<void(const string &mmapID)> g_contentHandler = nullptr;
static void MyContentChangeHandler(const std::string &mmapID) {
if (g_contentHandler) {
g_contentHandler(mmapID);
}
}
PYBIND11_MODULE(mmkv, m) {
m.doc() = "An efficient, small key-value storage framework developed by WeChat Team.";
py::enum_<MMKVMode>(m, "MMKVMode")
.value("SingleProcess", MMKVMode::MMKV_SINGLE_PROCESS)
.value("MultiProcess", MMKVMode::MMKV_MULTI_PROCESS)
.export_values();
py::enum_<MMKVLogLevel>(m, "MMKVLogLevel")
.value("NoLog", MMKVLogLevel::MMKVLogNone)
.value("Debug", MMKVLogLevel::MMKVLogDebug)
.value("Info", MMKVLogLevel::MMKVLogInfo)
.value("Warning", MMKVLogLevel::MMKVLogWarning)
.value("Error", MMKVLogLevel::MMKVLogError)
.export_values();
py::enum_<SyncFlag>(m, "SyncFlag")
.value("Sync", SyncFlag::MMKV_SYNC)
.value("ASync", SyncFlag::MMKV_ASYNC)
.export_values();
py::enum_<MMKVRecoverStrategic>(m, "MMKVRecoverStrategic")
.value("OnErrorDiscard", MMKVRecoverStrategic::OnErrorDiscard)
.value("OnErrorRecover", MMKVRecoverStrategic::OnErrorRecover)
.export_values();
py::enum_<MMKVErrorType>(m, "MMKVErrorType")
.value("CRCCheckFail", MMKVErrorType::MMKVCRCCheckFail)
.value("FileLength", MMKVErrorType::MMKVFileLength)
.export_values();
py::class_<MMKV, unique_ptr<MMKV, py::nodelete>> clsMMKV(m, "MMKV");
// TODO: not working
// clsMMKV.def(py::init(&MMKV::mmkvWithID),
// py::arg("mmapID"),
// py::arg("mode") = MMKV_SINGLE_PROCESS,
// py::arg("cryptKey") = (string*) nullptr,
// py::arg("rootDir") = (string*) nullptr);
clsMMKV.def(py::init([](const string &mmapID, MMKVMode mode, const string &cryptKey, const wstring &rootDir,
const size_t expectedCapacity) {
string *cryptKeyPtr = (cryptKey.length() > 0) ? (string *) &cryptKey : nullptr;
wstring *rootDirPtr = (rootDir.length() > 0) ? (wstring *) &rootDir : nullptr;
return MMKV::mmkvWithID(mmapID, mode, cryptKeyPtr, rootDirPtr, expectedCapacity);
}),
"Parameters:\n"
" mmapID: all instances of the same mmapID share the same data and file storage\n"
" mode: pass MMKVMode.MultiProcess for a multi-process MMKV\n"
" cryptKey: pass a non-empty string for an encrypted MMKV, 16 bytes at most\n"
" rootDir: custom root directory",
" expectedCapacity: the file size you expected when opening or creating file", py::arg("mmapID"),
py::arg("mode") = MMKV_SINGLE_PROCESS, py::arg("cryptKey") = string(), py::arg("rootDir") = string(),
py::arg("expectedCapacity") = 0);
clsMMKV.def("__eq__", [](MMKV &kv, const MMKV &other) { return kv.mmapID() == other.mmapID(); });
clsMMKV.def_static(
"initializeMMKV",
[](const wstring &rootDir, MMKVLogLevel logLevel, decltype(g_logHandler) logHandler) {
if (logHandler) {
g_logHandler = std::move(logHandler);
MMKV::initializeMMKV(rootDir, logLevel, MyLogHandler);
} else {
MMKV::initializeMMKV(rootDir, logLevel, nullptr);
}
},
"must call this before getting any MMKV instance", py::arg("rootDir"), py::arg("logLevel") = MMKVLogNone,
py::arg("log_handler") = nullptr);
clsMMKV.def_static(
"defaultMMKV",
[](MMKVMode mode, const string &cryptKey) {
string *cryptKeyPtr = (cryptKey.length() > 0) ? (string *) &cryptKey : nullptr;
return MMKV::defaultMMKV(mode, cryptKeyPtr);
},
"a generic purpose instance", py::arg("mode") = MMKV_SINGLE_PROCESS, py::arg("cryptKey") = string());
clsMMKV.def("mmapID", &MMKV::mmapID);
clsMMKV.def_readonly("isInterProcess", &MMKV::m_isInterProcess);
clsMMKV.def("cryptKey", &MMKV::cryptKey);
clsMMKV.def("reKey", &MMKV::reKey,
"transform plain text into encrypted text, or vice versa with an empty cryptKey\n"
"Parameters:\n"
" newCryptKey: 16 bytes at most",
py::arg("newCryptKey"));
clsMMKV.def("checkReSetCryptKey", &MMKV::checkReSetCryptKey,
"just reset cryptKey (will not encrypt or decrypt anything),\n"
"usually you should call this method after other process reKey() a multi-process mmkv",
py::arg("newCryptKey"));
// TODO: Doesn't work, why?
// clsMMKV.def("set", py::overload_cast<bool, const string&>(&MMKV::set), py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(bool, const string &))(&MMKV::set), "encode a boolean value", py::arg("value"),
py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(bool, const string &, uint32_t))(&MMKV::set),
"encode a boolean value with expiration", py::arg("value"), py::arg("key"), py::arg("expireDuration"));
clsMMKV.def("set", (bool (MMKV::*)(int32_t, const string &))(&MMKV::set), "encode an int32 value", py::arg("value"),
py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(int32_t, const string &, uint32_t))(&MMKV::set),
"encode an int32 value with expiration", py::arg("value"), py::arg("key"), py::arg("expireDuration"));
clsMMKV.def("set", (bool (MMKV::*)(uint32_t, const string &))(&MMKV::set), "encode an unsigned int32 value",
py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(uint32_t, const string &, uint32_t))(&MMKV::set),
"encode an unsigned int32 value with expiration", py::arg("value"), py::arg("key"),
py::arg("expireDuration"));
clsMMKV.def("set", (bool (MMKV::*)(int64_t, const string &))(&MMKV::set), "encode an int64 value", py::arg("value"),
py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(int64_t, const string &, uint32_t))(&MMKV::set),
"encode an int64 value with expiration", py::arg("value"), py::arg("key"), py::arg("expireDuration"));
clsMMKV.def("set", (bool (MMKV::*)(uint64_t, const string &))(&MMKV::set), "encode an unsigned int64 value",
py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(uint64_t, const string &, uint32_t))(&MMKV::set),
"encode an unsigned int64 value with expiration", py::arg("value"), py::arg("key"),
py::arg("expireDuration"));
//clsMMKV.def("set", (bool (MMKV::*)(float, const string &))(&MMKV::set), py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(double, const string &))(&MMKV::set), "encode a float/double value",
py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(double, const string &, uint32_t))(&MMKV::set),
"encode a float/double value with expiration", py::arg("value"), py::arg("key"),
py::arg("expireDuration"));
//clsMMKV.def("set", (bool (MMKV::*)(const char*, const string&))(&MMKV::set), py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(const string &, const string &))(&MMKV::set),
"encode an UTF-8 String/bytes value", py::arg("value"), py::arg("key"));
clsMMKV.def("set", (bool (MMKV::*)(const string &, const string &, uint32_t))(&MMKV::set),
"encode an UTF-8 String/bytes value with expiration", py::arg("value"), py::arg("key"),
py::arg("expireDuration"));
#if PY_MAJOR_VERSION >= 3
clsMMKV.def(
"set", [](MMKV &kv, const py::bytes &value, const string &key) { return kv.set(pyBytes2MMBuffer(value), key); },
"encode a bytes value", py::arg("value"), py::arg("key"));
clsMMKV.def(
"set",
[](MMKV &kv, const py::bytes &value, const string &key, uint32_t expireDuration) {
return kv.set(pyBytes2MMBuffer(value), key, expireDuration);
},
"encode a bytes value with expiration", py::arg("value"), py::arg("key"), py::arg("expireDuration"));
#endif
clsMMKV.def("getBool", &MMKV::getBool, "decode a boolean value", py::arg("key"), py::arg("defaultValue") = false,
py::arg("hasValue") = nullptr);
clsMMKV.def("getInt", &MMKV::getInt32, "decode an int32 value", py::arg("key"), py::arg("defaultValue") = 0,
py::arg("hasValue") = nullptr);
clsMMKV.def("getUInt", &MMKV::getUInt32, "decode an unsigned int32 value", py::arg("key"),
py::arg("defaultValue") = 0, py::arg("hasValue") = nullptr);
clsMMKV.def("getLongInt", &MMKV::getInt64, "decode an int64 value", py::arg("key"), py::arg("defaultValue") = 0,
py::arg("hasValue") = nullptr);
clsMMKV.def("getLongUInt", &MMKV::getUInt64, "decode an unsigned int64 value", py::arg("key"),
py::arg("defaultValue") = 0, py::arg("hasValue") = nullptr);
//clsMMKV.def("getFloat", &MMKV::getFloat, py::arg("key"), py::arg("defaultValue") = 0);
clsMMKV.def("getFloat", &MMKV::getDouble, "decode a float/double value", py::arg("key"),
py::arg("defaultValue") = 0, py::arg("hasValue") = nullptr);
clsMMKV.def(
"getString",
[](MMKV &kv, const string &key, const string &defaultValue) {
string result;
if (kv.getString(key, result)) {
return result;
}
return defaultValue;
},
"decode an UTF-8 String/bytes value", py::arg("key"), py::arg("defaultValue") = string());
clsMMKV.def(
"getBytes",
[](MMKV &kv, const string &key, const py::bytes &defaultValue) {
MMBuffer result = kv.getBytes(key);
if (result.length() > 0) {
return py::bytes((const char *) result.getPtr(), result.length());
}
return defaultValue;
},
"decode a bytes value", py::arg("key"), py::arg("defaultValue") = py::bytes());
clsMMKV.def("__contains__", &MMKV::containsKey, py::arg("key"));
clsMMKV.def("keys", &MMKV::allKeys, py::arg("filterExpire") = false);
clsMMKV.def("count", &MMKV::count, py::arg("filterExpire") = false);
clsMMKV.def("totalSize", &MMKV::totalSize);
clsMMKV.def("actualSize", &MMKV::actualSize);
clsMMKV.def("remove", &MMKV::removeValueForKey, py::arg("key"));
clsMMKV.def("remove", &MMKV::removeValuesForKeys, py::arg("keys"));
clsMMKV.def("clearAll", &MMKV::clearAll, py::arg("keepSpace") = false, "remove all key-values");
clsMMKV.def("trim", &MMKV::trim, "call this method after lots of removing if you care about disk usage");
clsMMKV.def("clearMemoryCache", &MMKV::clearMemoryCache, "call this method if you are facing memory-warning");
clsMMKV.def("sync", &MMKV::sync, py::arg("flag") = MMKV_SYNC,
"this call is not necessary unless you worry about unexpected shutdown of the machine (running out of "
"battery, etc)");
clsMMKV.def("enableAutoKeyExpire", &MMKV::enableAutoKeyExpire, py::arg("expireDurationInSecond"),
"turn on auto key expiration, passing 0 means never expire");
clsMMKV.def("disableAutoKeyExpire", &MMKV::disableAutoKeyExpire, "turn off auto key expiration");
clsMMKV.def("enableCompareBeforeSet", &MMKV::enableCompareBeforeSet, "turn on compare before set/update");
clsMMKV.def("disableCompareBeforeSet", &MMKV::disableCompareBeforeSet, "turn off compare before set/update");
clsMMKV.def("lock", &MMKV::lock, "get exclusive access, won't return until the lock is obtained");
clsMMKV.def("unlock", &MMKV::unlock);
clsMMKV.def("try_lock", &MMKV::try_lock, "try to get exclusive access");
// log callback handler
clsMMKV.def_static(
"registerLogHandler",
[](decltype(g_logHandler) callback) {
g_logHandler = std::move(callback);
MMKV::registerLogHandler(MyLogHandler);
},
"call this method to redirect MMKV's log,\n"
"must call MMKV.unRegisterLogHandler() or MMKV.onExit() before exit\n"
"Parameters:\n"
" log_handler: (logLevel: mmkv.MMKVLogLevel, file: str, line: int, function: str, message: str) -> None",
py::arg("log_handler"));
clsMMKV.def_static(
"unRegisterLogHandler",
[] {
g_logHandler = nullptr;
MMKV::unRegisterLogHandler();
},
"If you have registered a log handler, you must call this method or MMKV.onExit() before exit. "
"Otherwise your app/script won't exit properly.");
// error callback handler
clsMMKV.def_static(
"registerErrorHandler",
[](decltype(g_errorHandler) callback) {
g_errorHandler = std::move(callback);
MMKV::registerErrorHandler(MyErrorHandler);
},
"call this method to handle MMKV failure,\n"
"must call MMKV.unRegisterErrorHandler() or MMKV.onExit() before exit\n"
"Parameters:\n"
" error_handler: (mmapID: str, errorType: mmkv.MMKVErrorType) -> mmkv.MMKVRecoverStrategic",
py::arg("error_handler"));
clsMMKV.def_static(
"unRegisterErrorHandler",
[] {
g_errorHandler = nullptr;
MMKV::unRegisterErrorHandler();
},
"If you have registered an error handler, you must call this method or MMKV.onExit() before exit. "
"Otherwise your app/script won't exit properly.");
// content change callback handler
clsMMKV.def("checkContentChanged", &MMKV::checkContentChanged, "check if content been changed by other process");
clsMMKV.def_static(
"registerContentChangeHandler",
[](decltype(g_contentHandler) callback) {
g_contentHandler = std::move(callback);
MMKV::registerContentChangeHandler(MyContentChangeHandler);
},
"register a content change handler,\n"
"get notified when an MMKV instance has been changed by other process (not guarantee real-time notification),\n"
"must call MMKV.unRegisterContentChangeHandler() or MMKV.onExit() before exit\n"
"Parameters:\n"
" content_change_handler: (mmapID: str) -> None",
py::arg("content_change_handler"));
clsMMKV.def_static(
"unRegisterContentChangeHandler",
[] {
g_contentHandler = nullptr;
MMKV::unRegisterContentChangeHandler();
},
"If you have registered a content change handler, you must call this method or MMKV.onExit() before exit. "
"Otherwise your app/script won't exit properly.");
clsMMKV.def_static(
"onExit",
[] {
MMKV::onExit();
g_logHandler = nullptr;
g_errorHandler = nullptr;
g_contentHandler = nullptr;
},
"call this method before exit, especially if you have registered any callback handlers");
clsMMKV.def_static(
"backupOneToDirectory",
[](const string &mmapID, const wstring &dstDir, const wstring &srcDir) {
wstring *srcDirPtr = (srcDir.length() > 0) ? (wstring *) &srcDir : nullptr;
return MMKV::backupOneToDirectory(mmapID, dstDir, srcDirPtr);
},
"backup one MMKV instance from srcDir (default to the root dir of MMKV) to dstDir", py::arg("mmapID"),
py::arg("dstDir"), py::arg("srcDir") = string());
clsMMKV.def_static(
"restoreOneFromDirectory",
[](const string &mmapID, const wstring &srcDir, const wstring &dstDir) {
wstring *dstDirPtr = (dstDir.length() > 0) ? (wstring *) &dstDir : nullptr;
return MMKV::restoreOneFromDirectory(mmapID, srcDir, dstDirPtr);
},
"restore one MMKV instance from srcDir to dstDir (default to the root dir of MMKV)", py::arg("mmapID"),
py::arg("srcDir"), py::arg("dstDir") = string());
clsMMKV.def_static(
"backupAllToDirectory",
[](const wstring &dstDir, const string &srcDir) {
wstring *srcDirPtr = (srcDir.length() > 0) ? (wstring *) &srcDir : nullptr;
return MMKV::backupAllToDirectory(dstDir, srcDirPtr);
},
"backup all MMKV instance from srcDir (default to the root dir of MMKV) to dstDir", py::arg("dstDir"),
py::arg("srcDir") = string());
clsMMKV.def_static(
"restoreAllFromDirectory",
[](const wstring &srcDir, const wstring &dstDir) {
wstring *dstDirPtr = (dstDir.length() > 0) ? (wstring *) &dstDir : nullptr;
return MMKV::restoreAllFromDirectory(srcDir, dstDirPtr);
},
"restore all MMKV instance from srcDir to dstDir (default to the root dir of MMKV)", py::arg("srcDir"),
py::arg("dstDir") = string());
clsMMKV.def_static(
"removeStorage",
[](const string &mmapID, const wstring &rootDir) {
wstring *rootDirPtr = (rootDir.length() > 0) ? (wstring *) &rootDir : nullptr;
return MMKV::removeStorage(mmapID, rootDirPtr);
},
"remove the storage of the MMKV, including the data file & meta file (.crc)", py::arg("mmapID"),
py::arg("rootDir") = string());
}
-76
View File
@@ -1,76 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import sys
import platform
import subprocess
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
from distutils.version import LooseVersion
class CMakeExtension(Extension):
def __init__(self, name, sourcedir=''):
Extension.__init__(self, name, sources=[])
self.sourcedir = os.path.abspath(sourcedir)
class CMakeBuild(build_ext):
def run(self):
try:
out = subprocess.check_output(['cmake', '--version'])
except OSError:
raise RuntimeError("CMake must be installed to build the following extensions: " +
", ".join(e.name for e in self.extensions))
if platform.system() == "Windows":
cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1))
if cmake_version < '3.1.0':
raise RuntimeError("CMake >= 3.1.0 is required on Windows")
for ext in self.extensions:
self.build_extension(ext)
def build_extension(self, ext):
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
# required for auto-detection of auxiliary "native" libs
if not extdir.endswith(os.path.sep):
extdir += os.path.sep
cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
'-DPYTHON_EXECUTABLE=' + sys.executable]
cfg = 'Debug' if self.debug else 'Release'
build_args = ['--config', cfg]
if platform.system() == "Windows":
cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
if sys.maxsize > 2 ** 32:
cmake_args += ['-A', 'x64']
build_args += ['--', '/m']
else:
cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
build_args += ['--', '-j8']
env = os.environ.copy()
env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), self.distribution.get_version())
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)
subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)
setup(
name='mmkv',
version='1.2.13',
author='Tencent',
description='An efficient, small key-value storage framework developed by WeChat Team.',
long_description='The MMKV, for Python. MMKV is an efficient, generic, easy-to-use key-value storage framework '
'developed by the WeChat Team. It can be a replacement for SQLite.',
ext_modules=[CMakeExtension('mmkv')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
)
-209
View File
@@ -1,209 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import mmkv
import tempfile
KeyNotExist = 'KeyNotExist'
def test_bool(kv):
ret = kv.set(True, 'bool')
assert ret
value = kv.getBool('bool')
assert value
value = kv.getBool(KeyNotExist)
assert not value
value = kv.getBool(KeyNotExist, True)
assert value
print('test bool: passed')
def test_int(kv):
MinInt = -1 * (2 ** 31)
ret = kv.set(MinInt, 'Int')
assert ret
value = kv.getInt('Int')
assert value == MinInt
MaxInt = (2 ** 31) - 1
ret = kv.set(MaxInt, 'Int')
assert ret
value = kv.getInt('Int')
assert value == MaxInt
value = kv.getInt(KeyNotExist)
assert value == 0
value = kv.getInt(KeyNotExist, -1)
assert value == -1
print('test int: passed')
def test_uint(kv):
MaxUInt = (2 ** 32) - 1
ret = kv.set(MaxUInt, 'UInt')
assert ret
value = kv.getUInt('UInt')
assert value == MaxUInt
value = kv.getUInt(KeyNotExist)
assert value == 0
value = kv.getUInt(KeyNotExist, 1)
assert value == 1
print('test unsigned int: passed')
def test_long_int(kv):
MinLongInt = -1 * (2 ** 63)
ret = kv.set(MinLongInt, 'LongInt')
assert ret
value = kv.getLongInt('LongInt')
assert value == MinLongInt
MaxLongInt = (2 ** 63) - 1
ret = kv.set(MaxLongInt, 'LongInt')
assert ret
value = kv.getLongInt('LongInt')
assert value == MaxLongInt
value = kv.getLongInt(KeyNotExist)
assert value == 0
value = kv.getLongInt(KeyNotExist, -1)
assert value == -1
print('test long int: passed')
def test_long_uint(kv):
MaxLongUInt = (2 ** 64) - 1
ret = kv.set(MaxLongUInt, 'LongUInt')
assert ret
value = kv.getLongUInt('LongUInt')
assert value == MaxLongUInt
value = kv.getLongUInt(KeyNotExist)
assert value == 0
value = kv.getLongUInt(KeyNotExist, 1)
assert value == 1
print('test long unsigned int: passed')
def is_float_equal(a, b, accuracy=1e-09):
return abs(a - b) <= max(accuracy * max(abs(a), abs(b)), 0.0)
def test_float(kv):
MinFloat = sys.float_info.min
ret = kv.set(MinFloat, 'Float')
assert ret
value = kv.getFloat('Float')
assert is_float_equal(value, MinFloat)
MaxFloat = sys.float_info.max
ret = kv.set(MaxFloat, 'Float')
assert ret
value = kv.getFloat('Float')
assert is_float_equal(value, MaxFloat)
value = kv.getFloat(KeyNotExist)
assert is_float_equal(value, 0.0)
value = kv.getFloat(KeyNotExist, -1.0)
assert is_float_equal(value, -1.0)
print('test float: passed')
def test_string(kv):
OlympicString = 'Hello 2021 Olympic Games 奥运会'
ret = kv.set(OlympicString, 'String')
assert ret
value = kv.getString('String')
if sys.version_info >= (3, 0):
assert value == OlympicString
else:
assert value == OlympicString.decode('utf-8')
value = kv.getString(KeyNotExist)
assert (not value) or (len(value) == 0)
value = kv.getString(KeyNotExist, 'a')
assert value == 'a'
print('test string: passed')
def test_bytes(kv):
OlympicYears = [0, 4, 8, 12, 16, 21]
OlympicYearsBytes = bytes(OlympicYears)
ret = kv.set(OlympicYearsBytes, 'Bytes')
assert ret
value = kv.getBytes('Bytes')
assert value == OlympicYearsBytes
if sys.version_info >= (3, 0):
value = list(value)
assert value == OlympicYears
value = kv.getBytes(KeyNotExist)
assert (not value) or (len(value) == 0)
default_value = None
if sys.version_info >= (3, 0):
default_value = bytes('哦豁', 'utf8')
else:
default_value = bytes('a')
value = kv.getBytes(KeyNotExist, default_value)
assert value == default_value
print('test bytes: passed')
def test_equal(kv, mmap_id):
assert kv.mmapID() == mmap_id
another = mmkv.MMKV(mmap_id)
assert kv == another
kv_keys = sorted(kv.keys())
another_keys = sorted(another.keys())
assert kv_keys == another_keys
print('test equal: passed')
if __name__ == '__main__':
temp_dir = tempfile.gettempdir()
temp_dir = temp_dir + '/mmkv'
mmkv.MMKV.initializeMMKV(temp_dir)
kv = mmkv.MMKV('unit_test_python')
test_bool(kv)
test_int(kv)
test_uint(kv)
test_long_int(kv)
test_long_uint(kv)
test_float(kv)
test_string(kv)
test_bytes(kv)
test_equal(kv, 'unit_test_python')