69 Commits
Author SHA1 Message Date
Oliver Hamlet 01a8567ed8 Move most reference docs into source as docstrings
It's easier to maintain, though there seems to be a bug in pybind11, as
it doesn't use the docstrings I've provided for some static read-only
properties, so Version and WrapperVersion are still documented
separately.

It's not related, but I've noticed that for some reason intersphinx
is unable to turn :cpp: domain references into hyperlinks, but no
related information is logged to say why.
2020-01-04 16:07:36 +00:00
Oliver Hamlet 7ccfe6d7f4 Expose a few more GameInterface and PluginInterface methods
The exposed methods are:

* GameInterface::LoadPlugins
* GameInterface::GetPlugin
* PluginInterface::GetName
* PluginInterface::IsMaster
* PluginInterface::IsLightMaster
* PluginInterface::IsValidAsLightMaster
2020-01-04 16:07:36 +00:00
Oliver Hamlet 2106ab4f01 Fix Python GIL causing a hang when running the logging callback
pybind11 holds the GIL whenever a C++ function is called from Python, so
the logging callback can't then execute Python code. To avoid this
problem, release the GIL for every function bound by pybind11 (in case
log statements are added to those that don't currently have any), and
wrap the logging callback in a lambda function that first aquires the
GIL lock for the function's scope.
2020-01-04 12:37:00 +00:00
Oliver Hamlet dcf9b8b96b Rename package and repository to libloot-python and module to loot 2019-11-06 20:17:40 +00:00
Oliver Hamlet 263322a75c Update libloot to v0.15.0 2019-11-06 18:58:02 +00:00
Oliver Hamlet b52cd9bd6b Update pybind11 to v2.4.2
v2.3.0 fixed GIL handling bugs, which means we need to explicitly stop
storing the Python logging callback before exiting, or the exit will
hang.
2019-10-22 17:43:30 +01:00
Oliver Hamlet 580bf87802 Fix artifact name matching on AppVeyor 2019-10-21 18:08:20 +01:00
Oliver Hamlet c144f7a66c Fix forwardslashes in package name on AppVeyor 2019-10-21 18:08:18 +01:00
Daniel Nunes ffd62f9e0c Include setup.py and __init__.py in archive for pip
So that the wrapper library can be installed with `pip install
<archive>`. The setup.py is Windows-specific, but CI only
produces Windows builds so that's not currently an issue.

The __init__.py re-exports the .pyd module so that the .pyd and
.dll can be installed into a subdirectory of site-packages for
neatness, and so that the installed module isn't just to work
around pip install being unable to install a pyd with no package.
2019-10-21 18:07:55 +01:00
Oliver Hamlet 937a36a5ae Set platform architecture using CMake's -A option
Introduced in CMake v3.1.0, I think it's a bit easier to remember than a
suffix on the generator name.
2019-06-22 13:46:43 +01:00
Oliver Hamlet 915c002ca6 Use PYBIND11_MODULE instead of PYBIND11_PLUGIN
The latter is deprecated.
2019-06-16 13:29:31 +01:00
Oliver Hamlet c19a9be579 Run tests using ctest on AppVeyor 2019-06-16 13:16:35 +01:00
Oliver Hamlet b393507657 Update libloot to v0.14.7 2019-06-16 13:03:53 +01:00
Oliver Hamlet d356ac2445 Set version number to 4.0.2 2019-03-25 09:47:46 +00:00
Oliver Hamlet fe4c3ab6b5 Update libloot to v0.14.5 2019-03-25 09:47:03 +00:00
Oliver Hamlet b17b531073 Fix get_plugin_cleanliness never returning dirty 2019-03-25 09:01:29 +00:00
Oliver Hamlet 06a6d9ceb6 Use CTest to run Python tests
Avoid having to copy the test script by modifying PYTHONPATH at runtime,
and move the test script into the test directory.
2019-03-25 08:38:34 +00:00
Oliver Hamlet a35abab104 Add a test masterlist
It'll be extended with more test metadata in future.
2019-03-25 08:20:03 +00:00
Oliver Hamlet 646a8d02e0 Add .vscode/ to .gitignore 2019-03-25 08:06:31 +00:00
Oliver Hamlet cab63886d3 Set version number to 4.0.1 2019-01-27 14:53:14 +00:00
Oliver Hamlet 8a61701855 Update libloot to v0.14.3 2019-01-27 14:52:03 +00:00
Oliver Hamlet 8c75e83374 Update a few references to MSVC 2015 redist to be 2017 2019-01-27 14:50:34 +00:00
Oliver Hamlet 5450709ebe Update docs 2019-01-12 17:16:43 +00:00
Oliver Hamlet 3a9ff95de2 Update wrapper version to 4.0.0 2019-01-12 17:14:12 +00:00
Oliver Hamlet 1298fce6fd Update libloot to v0.14.1 2019-01-12 15:55:22 +00:00
Oliver Hamlet 21242f359d Update readme 2018-12-13 17:46:21 +00:00
Oliver Hamlet 6579eb5178 Build for Python 2.7 and 3.7 on AppVeyor 2018-12-13 17:20:57 +00:00
Oliver Hamlet 61a74b6984 Build 32-bit and 64-bit binaries on AppVeyor 2018-12-13 08:08:58 +00:00
Oliver Hamlet 66f84d4848 Support 64-bit builds 2018-12-13 08:05:01 +00:00
Oliver Hamlet 5f5c0e3949 Update pybind11 to fix AppVeyor build
Utumno experienced issues with Python modules built with more recent
versions of pybind11, which is why I tried to avoid this, but it looks
like it's necessary.
2018-12-12 19:59:14 +00:00
Oliver Hamlet ab926c8755 Use masterlist with fixed syntax
The older masterlist had parentheses between not and a function, which
has never been valid, but which only caused an error starting with
v0.14.0.
2018-12-12 19:57:34 +00:00
Oliver Hamlet 35475b292f Update to libloot v0.14.0
libloot is the renamed LOOT API. This adds a requirement on C++17,
and so MSVC 2017 on Windows.

pybind11 doesn't provide handling for std::filesystem::path, so wrapper
functions are used to perform the necessary one-way conversion without
having to expose the complexity on the Python side.
2018-12-12 19:33:21 +00:00
Oliver Hamlet 51e2e4e4fa Remove Travis badge
Travis hasn't been used for a while now.
2018-11-07 17:26:39 +00:00
Oliver Hamlet c17437a579 Fix AppVeyor CI link
Also update a GitHub URL, replacing WrinklyNinja with Ortham.
2018-11-07 16:58:07 +00:00
Oliver Hamlet 49204621c5 Fix typo in bintray delete script call 2018-08-12 22:26:55 +01:00
Oliver Hamlet 3098ffa692 Update Bintray delete script to fix deleting old builds 2018-08-12 21:47:54 +01:00
Oliver Hamlet 392a090823 Re-encrypt Bintray and GitHub auth tokens
Using the values encrypted by AppVeyor when I'm logged in as myself for
the LOOT account.
2018-08-12 21:47:02 +01:00
Oliver Hamlet e98b43a4b1 Remove old builds from LOOT's Bintray org 2018-08-08 21:39:24 +01:00
Oliver Hamlet a957ece62b Upload artifacts to repo in LOOT's Bintray org 2018-08-06 21:07:20 +01:00
Oliver Hamlet f97de90e02 Update wrapper version to 3.1.1 2018-05-12 21:50:30 +01:00
Oliver Hamlet d00c59b7b2 Update LOOT API to v0.13.2 2018-05-12 21:15:17 +01:00
Oliver Hamlet e06227f437 Build with debug info and package PDB 2018-05-12 19:26:21 +01:00
Oliver Hamlet bcc536d4b0 Downgrade to pybind11 v2.1.1
Updating to v2.2.1 introduced a runtime issue that's
environment-specific but it's not clear what the issue is, so
just downgrade for now.
2018-05-12 19:12:47 +01:00
Oliver Hamlet a564f230be Update wrapper version to 3.1.0
Although the LOOT API update includes breaking changes, none of them are
exposed by the Python wrapper, so this is just a minor update.
2018-04-15 13:16:49 +01:00
Oliver Hamlet 27c8f58494 Update pybind11 to v2.2.2 2018-04-15 13:16:49 +01:00
Oliver Hamlet 179125f006 Expose GameType::tes5vr and GameType::fo4vr 2018-04-15 13:16:49 +01:00
Oliver Hamlet 671ca81beb Update to LOOT API v0.13.1 2018-04-15 13:16:49 +01:00
Oliver Hamlet 7faa360790 Update outdated usage example 2017-12-30 15:29:21 +00:00
Oliver Hamlet 0c9d014174 Remove default value for create_game_handle's game_path argument
The LOOT API incorrectly allows it, it causes a runtime exception.
2017-12-30 15:29:03 +00:00
Oliver Hamlet a8e2b11280 Allow initialise_locale() to be called with no args
Use a blank string as the default argument, as it's the most commonly desired value.
2017-12-30 15:27:48 +00:00
Oliver Hamlet 1275a11be7 Expose load_current_load_order_state() via GameInterface 2017-11-04 17:27:15 +00:00
Oliver Hamlet 5357858069 Update documentation 2017-11-04 13:52:19 +00:00
Oliver Hamlet 04088ac8f4 Update wrapper version to 3.0.0 2017-11-04 12:29:27 +00:00
Oliver Hamlet 7098671ad5 Update to LOOT API v0.12.0
Significant changes include:

- The logging interface has changed, a callback can now be set
  instead of an output file and verbosity, giving clients more
  control.
- DatabaseInterface::EvalLists() has been removed.
- SimpleMessage now has a condition field.
2017-11-04 12:14:26 +00:00
Oliver Hamlet 212f899396 Update to pybind11 v2.2.1 2017-10-17 19:57:42 +01:00
Oliver Hamlet 1e0dbe8dc2 Update to LOOT API v0.11.1 and fix Linux builds 2017-10-17 20:34:45 +01:00
Oliver Hamlet abbc0892fd Update wrapper version to 2.0.0 2017-05-13 08:46:04 +01:00
Oliver Hamlet a94fb19bde Update to LOOT API v0.11.0
This is a breaking change:
- LanguageCode has been removed
- The SimpleMessage Python class is now "SimpleMessage", from "Message"
- DatabaseInterface::sort_plugins() has been removed
- DatabaseInterface::get_plugin_messages() has been replaced with
DatabaseInterface::get_plugin_metadata() followed by
PluginMetadata::get_simple_messages()
- set_logging_verbosity(), set_log_file() and initialise_locale() have
been added

Not everything in the v0.11 API has been wrapped, to limit the potential
for future breaking changes.

The PluginTags struct and PluginCleanliness enum and their associated
functions have been re-implemented as part of the Python wrapper as they
were removed from the API due to no longer exposing any additional
functionality but are still useful.
2017-05-13 08:41:42 +01:00
Oliver Hamlet 2823db915a Update to pybind11 v2.1.1 2017-05-13 08:19:30 +01:00
Oliver Hamlet 70371b7647 Deploy tagged builds to GitHub 2016-12-15 20:45:38 +00:00
Oliver Hamlet f5cfa7cc0f Delete old Bintray versions on successful CI run 2016-12-15 20:44:55 +00:00
Oliver Hamlet cf391b0da0 Update to LOOT API v0.10.2 and remove Linux builds
Support for Linux builds is removed as LOOT's snapshot builds are no
longer reliably available, and Linux builds aren't published as part of
LOOT releases. Linux builds could easily be reinstated if LOOT API
binaries become reliably available again.
2016-12-15 20:38:44 +00:00
Oliver Hamlet 58dd19af7d Update Bintray API token used
The previous token was leaked and has been invalidated.
2016-12-05 23:06:46 +00:00
Oliver Hamlet 0f27192051 Explicitly run cpack with Release config on Travis 2016-11-14 21:47:14 +00:00
Oliver Hamlet 852aee5e0c Append branch name to archive version on CI
To prevent artifact collisions when building fast-forward merged
commits.
2016-11-14 17:09:09 +00:00
Oliver Hamlet 96d4f37bb5 Increment version to 1.1.2 2016-11-13 15:42:41 +00:00
Oliver Hamlet 8685762ed9 Update LOOT API to v0.10.1 2016-11-13 15:42:41 +00:00
Oliver Hamlet 1fb6502293 Increment version to 1.1.1 2016-11-06 15:10:57 +00:00
Oliver Hamlet 8360465404 Add missing GameType::tes5se 2016-11-06 15:09:06 +00:00
23 changed files with 1021 additions and 543 deletions
+2
View File
@@ -50,3 +50,5 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk
.vscode/
-49
View File
@@ -1,49 +0,0 @@
sudo: false
language: cpp
compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test # ppa:ubuntu-toolchain-r/test
packages:
- gcc-5
- g++-5
- wget
install:
# Use GCC 5.
- if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi
# Download CMake binary because those available in PPAs either don't support
# xz downloads or don't support HTTPS.
- wget https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz
- tar -xf cmake-3.6.2-Linux-x86_64.tar.gz
before_script:
# Move into the cloned LOOT repo build path.
- mkdir build && cd build
# Link dynamically to the C++ standard library runtime.
- ../cmake-3.6.2-Linux-x86_64/bin/cmake ..
script:
- make all
- python -m unittest test
after_success:
# Get the full repo history before trying to run the archiver.
- git fetch --unshallow
- ../cmake-3.6.2-Linux-x86_64/bin/cpack
- cd $TRAVIS_BUILD_DIR
# Need to replace the Bintray config files' version placeholders.
- VERSION=$(ls build/package/loot_api_python-*.tar.xz | sed 's/build\/package\/loot_api_python-//;s/-Linux.tar.xz//;')
- sed -i "s/REPLACE_THIS_VERSION/${VERSION}_${TRAVIS_BRANCH}/" scripts/travis/loot-api.bintray.json
deploy:
- provider: bintray
file: scripts/travis/loot-api.bintray.json
user: wrinklyninja
key:
secure: "XUj1hk5lNhyzPNs3iv1kw8h370QrS+hOcGvj7fMkMb3KQF+I7XJIIhi5lcVd/X5NB79M4IYKac+7n4fVPsf6jXd+SpKYKdPF5ZnSn00dtU34Jhz+DiZQoLdAVLhGgOTLxv2SE6kfvfCXXVlSSoVOVNH0rx0OWE/BjZaSxd9+XeQgEJ/qozvt9kl4sd02y/M4r9h4SAMGf3zav+PWy+5cigy/LFONDQ9oeCEOJmVbazlB2Hkt0GnRkPpW/jFjUvt5qs0jS1neBBmeVCxvue1qJVJBwPtIszNhoARn/mY1tfQ4b0MdzvYhK1kjHvtBLW+OoXOr5VaiZ2a0J+t7jtgfO2cadiIgqCbxddltq1CESFa3pSCpkAfSg1jkl3Lj7BJzSLrRLxySN+Hq0O4qR3A3cQOVt5glZIbabq0h1b9jxZSpU9D+jwgZxxmI8NZJRS4TegZ+Q1ygY/MrSydUWQ3fK0ls20GePKjPtK90ipqt0HLyjAcLLVshc1jB3MPqDKEPXnQtxrJmbHwZu2rzlngIjUNybJXQxuJLqcE/awijpoftbqxctXzEq2hMj/lp6Y4agX++hikPg+BP6HHCwuwgXTYkcUXC6v5Mm2NTboDnimiJq2aCzcgc4RVjes1o5cKng8xtpYCNoTkEd4Tu1DL+AR6tJnjXkvRsJCdBWUVIFG4="
skip_cleanup: true
on:
all_branches: true
+90 -55
View File
@@ -1,15 +1,23 @@
cmake_minimum_required (VERSION 2.8.12)
project (loot_api_python)
cmake_minimum_required (VERSION 3.1)
project (libloot-python)
include(ExternalProject)
set(EXTERNAL_PROJECTS_PATH "${CMAKE_BINARY_DIR}/external/src")
make_directory(${EXTERNAL_PROJECTS_PATH})
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
##############################
# Get Build Revision
##############################
set(LIBLOOT_PY_VERSION_MAJOR 4)
set(LIBLOOT_PY_VERSION_MINOR 0)
set(LIBLOOT_PY_VERSION_PATCH 2)
find_package(Git)
IF (GIT_FOUND)
@@ -21,18 +29,21 @@ ELSE()
SET (GIT_COMMIT_STRING "unknown")
ENDIF ()
message(STATUS "libloot-python version: ${LIBLOOT_PY_VERSION_MAJOR}.${LIBLOOT_PY_VERSION_MINOR}.${LIBLOOT_PY_VERSION_PATCH}")
message(STATUS "Git revision: ${GIT_COMMIT_STRING}")
# Write to file.
configure_file("${CMAKE_SOURCE_DIR}/src/wrapper_version.cpp.in" "${CMAKE_BINARY_DIR}/generated/wrapper_version.cpp" @ONLY)
configure_file("${CMAKE_SOURCE_DIR}/python/setup.py" "${CMAKE_BINARY_DIR}/generated/setup.py" @ONLY)
#######################################
# pybind11
#######################################
set(PYBIND11_URL "https://github.com/pybind/pybind11/archive/v1.8.1.tar.gz")
set(PYBIND11_DOWNLOAD_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-1.8.1.tar.gz")
set(PYBIND11_EXTRACTED_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-1.8.1")
set(PYBIND11_VERSION "2.4.2")
set(PYBIND11_URL "https://github.com/pybind/pybind11/archive/v${PYBIND11_VERSION}.tar.gz")
set(PYBIND11_DOWNLOAD_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-${PYBIND11_VERSION}.tar.gz")
set(PYBIND11_EXTRACTED_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-${PYBIND11_VERSION}")
if (NOT EXISTS ${PYBIND11_DOWNLOAD_PATH})
file(DOWNLOAD ${PYBIND11_URL} ${PYBIND11_DOWNLOAD_PATH})
@@ -45,37 +56,44 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz ${PYBIND11_DOWNLOAD_PATH}
add_subdirectory(${PYBIND11_EXTRACTED_PATH})
#######################################
# LOOT API
# libloot
#######################################
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(LOOT_API_URL "https://bintray.com/wrinklyninja/loot/download_file?file_path=loot-api_0.10.0-0-g7b97241_dev.7z")
if (NOT "${CMAKE_GENERATOR_PLATFORM}" MATCHES "x64")
set(LIBLOOT_URL "https://github.com/loot/libloot/releases/download/0.15.0/libloot-0.15.0-0-g629fdab_master-win32.7z")
else()
set(LIBLOOT_URL "https://github.com/loot/libloot/releases/download/0.15.0/libloot-0.15.0-0-g629fdab_master-win64.7z")
endif()
else()
set(LOOT_API_URL "https://bintray.com/wrinklyninja/loot/download_file?file_path=loot-api_0.10.0-0-g7b97241_0.10.0.tar.xz")
set(LIBLOOT_URL "https://github.com/loot/libloot/releases/download/0.15.0/libloot.tar.xz")
endif()
ExternalProject_Add(loot-api-c++
ExternalProject_Add(libloot
PREFIX "external"
URL ${LOOT_API_URL}
URL ${LIBLOOT_URL}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
ExternalProject_Get_Property(loot-api-c++ SOURCE_DIR)
set(LOOT_API_EXTRACTED_PATH ${SOURCE_DIR})
ExternalProject_Get_Property(libloot SOURCE_DIR)
set(LIBLOOT_EXTRACTED_PATH ${SOURCE_DIR})
include_directories("${CMAKE_SOURCE_DIR}/src"
"${LOOT_API_EXTRACTED_PATH}/include")
link_directories(${LOOT_API_EXTRACTED_PATH})
set(LOOT_API_STATIC_LIBRARY "${CMAKE_STATIC_LIBRARY_PREFIX}loot_api${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(LOOT_API_SHARED_LIBRARY "${CMAKE_SHARED_LIBRARY_PREFIX}loot_api${CMAKE_SHARED_LIBRARY_SUFFIX}")
"${LIBLOOT_EXTRACTED_PATH}/include")
link_directories(${LIBLOOT_EXTRACTED_PATH})
set(LIBLOOT_STATIC_LIBRARY "${CMAKE_STATIC_LIBRARY_PREFIX}loot${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(LIBLOOT_SHARED_LIBRARY "${CMAKE_SHARED_LIBRARY_PREFIX}loot${CMAKE_SHARED_LIBRARY_SUFFIX}")
#######################################
# Test Masterlist
# testing-plugins
#######################################
ExternalProject_Add(test-masterlist
# This is a bit messy because the testing-plugins aren't actually required to
# build libloot-python, but having them as a dependency is the easiest way to
# manage downloading and moving them to the expected location.
ExternalProject_Add(testing-plugins
PREFIX "external"
URL "https://github.com/loot/oblivion/archive/09286601739c11a3a3e4552c6ddbdcaeffd1a8d3.zip"
URL "https://github.com/Ortham/testing-plugins/archive/1.4.1.tar.gz"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
@@ -84,14 +102,20 @@ ExternalProject_Add(test-masterlist
# Python Module
#######################################
pybind11_add_module(loot_api "${CMAKE_SOURCE_DIR}/src/main.cpp" "${CMAKE_BINARY_DIR}/generated/wrapper_version.cpp")
pybind11_add_module(libloot-python "${CMAKE_SOURCE_DIR}/src/main.cpp"
"${CMAKE_SOURCE_DIR}/src/convenience.cpp"
"${CMAKE_BINARY_DIR}/generated/wrapper_version.cpp")
add_dependencies(loot_api loot-api-c++ test-masterlist)
# Set the target output name to loot to follow common Python library naming
# conventions (no lib, no -python).
set_target_properties(libloot-python PROPERTIES OUTPUT_NAME loot)
add_dependencies(libloot-python libloot testing-plugins)
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(loot_api PRIVATE ${LOOT_API_STATIC_LIBRARY})
target_link_libraries(libloot-python PRIVATE ${LIBLOOT_STATIC_LIBRARY})
else()
target_link_libraries(loot_api PRIVATE ${LOOT_API_SHARED_LIBRARY})
target_link_libraries(libloot-python PRIVATE ${LIBLOOT_SHARED_LIBRARY})
endif()
########################################
@@ -100,37 +124,51 @@ endif()
find_package(PythonInterp REQUIRED)
# Copy the API binary to the build directory.
add_custom_command(TARGET loot_api POST_BUILD
# Copy the libloot binary to the build directory.
add_custom_command(TARGET libloot-python POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${LOOT_API_EXTRACTED_PATH}/${LOOT_API_SHARED_LIBRARY}"
"$<TARGET_FILE_DIR:loot_api>/${LOOT_API_SHARED_LIBRARY}")
"${LIBLOOT_EXTRACTED_PATH}/${LIBLOOT_SHARED_LIBRARY}"
"$<TARGET_FILE_DIR:libloot-python>/${LIBLOOT_SHARED_LIBRARY}")
# Copy the test masterlist to the build directory.
ExternalProject_Get_Property(test-masterlist SOURCE_DIR)
add_custom_command(TARGET loot_api POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SOURCE_DIR}/masterlist.yaml"
"$<TARGET_FILE_DIR:loot_api>/masterlist.yaml")
# Copy testing plugins
ExternalProject_Get_Property(testing-plugins SOURCE_DIR)
add_custom_command(TARGET libloot-python POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${SOURCE_DIR}
$<TARGET_FILE_DIR:libloot-python>)
# Also copy the test Python script to the build directory.
add_custom_command(TARGET loot_api POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/src/test.py"
"$<TARGET_FILE_DIR:loot_api>/test.py")
enable_testing()
add_test(NAME python
WORKING_DIRECTORY $<TARGET_FILE_DIR:libloot-python>
COMMAND "${PYTHON_EXECUTABLE}" ${CMAKE_SOURCE_DIR}/test/test.py)
########################################
# Install
########################################
install(TARGETS loot_api
DESTINATION "."
CONFIGURATIONS Release)
install(TARGETS libloot-python
DESTINATION "loot"
CONFIGURATIONS Release RelWithDebInfo)
install(FILES "${LOOT_API_EXTRACTED_PATH}/${LOOT_API_SHARED_LIBRARY}"
"${CMAKE_SOURCE_DIR}/docs/README.md"
install(FILES "${LIBLOOT_EXTRACTED_PATH}/${LIBLOOT_SHARED_LIBRARY}"
"${CMAKE_SOURCE_DIR}/python/loot/__init__.py"
DESTINATION "loot"
CONFIGURATIONS Release RelWithDebInfo)
install(FILES "${CMAKE_SOURCE_DIR}/docs/README.md"
"${CMAKE_BINARY_DIR}/generated/setup.py"
"${CMAKE_SOURCE_DIR}/LICENSE"
DESTINATION "."
CONFIGURATIONS Release)
CONFIGURATIONS Release RelWithDebInfo)
IF (MSVC)
install(FILES $<TARGET_PDB_FILE:libloot-python>
DESTINATION "loot"
OPTIONAL
CONFIGURATIONS RelWithDebInfo
RENAME libloot-python.pdb)
ENDIF ()
########################################
# CPack
@@ -145,22 +183,19 @@ IF (GIT_FOUND)
OUTPUT_VARIABLE GIT_DESCRIBE_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE)
IF (${GIT_DESCRIBE_STRING} MATCHES "HEAD")
IF ($ENV{APPVEYOR_REPO_BRANCH})
set(GIT_DESCRIBE_STRING $ENV{APPVEYOR_REPO_BRANCH})
ENDIF()
IF (DEFINED ENV{APPVEYOR_REPO_BRANCH})
set(GIT_DESCRIBE_STRING "${GIT_DESCRIBE_STRING}_$ENV{APPVEYOR_REPO_BRANCH}")
ELSEIF (DEFINED ENV{TRAVIS_BRANCH})
set(GIT_DESCRIBE_STRING "${GIT_DESCRIBE_STRING}_$ENV{TRAVIS_BRANCH}")
ENDIF()
string(REPLACE "/" "-" GIT_DESCRIBE_STRING ${GIT_DESCRIBE_STRING})
ELSE()
SET (GIT_DESCRIBE_STRING "unknown-version")
ENDIF ()
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CPACK_GENERATOR "7Z")
else()
set(CPACK_GENERATOR "TXZ")
endif()
set(CPACK_PACKAGE_VERSION ${GIT_DESCRIBE_STRING})
set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_VERSION "${GIT_DESCRIBE_STRING}-python$ENV{PYTHON_VERSION}")
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/package")
include(CPack)
+8 -9
View File
@@ -1,21 +1,20 @@
LOOT API Python Module
=======================
libloot-python
==============
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/loot/loot-api-python?branch=master&svg=true)](https://ci.appveyor.com/project/WrinklyNinja/loot-api-python)
[![Travis Build Status](https://travis-ci.org/loot/loot-api-python.svg?branch=master)](https://travis-ci.org/loot/loot-api-python)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/k2ugge3po7254o1o/branch/master?svg=true)](https://ci.appveyor.com/project/LOOT/libloot-python/branch/master)
[![Documentation Status](https://readthedocs.org/projects/loot-api-python/badge/)](http://loot-api-python.readthedocs.io/)
A Python module that wraps the LOOT API, generated by [pybind11](https://github.com/pybind/pybind11).
A Python module that wraps libloot, generated by [pybind11](https://github.com/pybind/pybind11). Not everything in the API is exposed: coverage can be extended on request (or by pull request).
## Downloads
Snapshot builds are available on [Bintray](https://bintray.com/wrinklyninja/loot/loot-api-python). The snapshot build archives are named like so:
Snapshot builds are available on [Bintray](https://bintray.com/loot/snapshots/libloot-python). The snapshot build archives are named like so:
```
loot_api_python-<short revision ID>-win32.7z
libloot-python-<short revision ID>-python<python version>-win<architecture>.zip
```
For example `loot_api_python-94de368-win32.7z` was built using the revision with shortened commit ID `94de368`.
For example `libloot-python-94de368-python2.7-win32.zip` was built using the revision with shortened commit ID `94de368`.
## Documentation
@@ -23,6 +22,6 @@ The documentation can be found online at [Read The Docs](http://loot-api-python.
## Build Instructions
The module's build system uses [CMake](https://cmake.org/) and requires [Python](https://www.python.org) (tested with 2.7). Make sure they're installed, then run CMake, and build the generated solution file.
The module's build system uses [CMake](https://cmake.org/) and requires [Python](https://www.python.org). Make sure they're installed, then run CMake, and build the generated solution file.
Only Windows support has been tested, though Linux builds should also be possible.
+58 -17
View File
@@ -1,42 +1,83 @@
os: Visual Studio 2015
os: Visual Studio 2017
version: "{build}-{branch}"
configuration: Release
configuration: RelWithDebInfo
platform:
- Win32
- x64
environment:
bintray_auth_token:
secure: PgsEA6TjHVf718zMnK7J/fT1hUAVNKBeWhpYgYaeCyeZk37VT4Ics6j7+B7ElLEr
github_auth_token:
secure: yDqT5l/e5MntbW99V6+MHlfFgNv+UIogFfeyUVqtFk5lFRB/dAraLLwKCLl6y+DH
matrix:
- PYTHON_VERSION: 2.7
- PYTHON_VERSION: 3.7
install:
- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/Ortham/ci-scripts/2.0.0/delete_old_bintray_versions.py', "$env:APPVEYOR_BUILD_FOLDER\delete_old_bintray_versions.py")
- ps: (New-Object Net.WebClient).DownloadFile('https://github.com/WrinklyNinja/testing-plugins/archive/1.4.1.zip', "$PWD/1.4.1.zip")
- 7z x 1.4.1.zip
- mv testing-plugins-1.4.1 testing-plugins
before_build:
- ps: $env:PYTHON_DIRECTORY_VERSION=$env:PYTHON_VERSION.Replace(".", "")
- cd %APPVEYOR_BUILD_FOLDER%
- ps: mkdir build
- cd build
- cmake .. -G "Visual Studio 14 2015"
- ps: |
if ($env:PLATFORM -eq 'Win32') {
cmake .. -G "Visual Studio 15 2017" -DPYTHON_EXECUTABLE="C:\Python${env:PYTHON_DIRECTORY_VERSION}\python.exe"
} else {
cmake .. -G "Visual Studio 15 2017" -A x64 -DPYTHON_EXECUTABLE="C:\Python${env:PYTHON_DIRECTORY_VERSION}-x64\python.exe"
}
build:
verbosity: minimal
project: 'c:\projects\loot-api-python\build\loot_api_python.sln'
project: 'c:\projects\libloot-python\build\libloot-python.sln'
test_script:
- cd %APPVEYOR_BUILD_FOLDER%\build\Release
- python -m unittest test
- cd %APPVEYOR_BUILD_FOLDER%\build
- ctest -C %CONFIGURATION%
after_test:
- cd %APPVEYOR_BUILD_FOLDER%\build
- cpack
# AppVeyor checks out a specific commit, but that means the archive script
# can't tell which branch it's on, so rename the 7z archives.
- cpack -C %CONFIGURATION%
- ps: $env:GIT_DESCRIBE = ((git describe --tags --long --always) | Out-String) -replace "`n|`r", ""
- ps: $env:PACKAGE_VERSION = $env:GIT_DESCRIBE + "_" + ($env:APPVEYOR_REPO_BRANCH.replace("/", "-"))
- ps: $env:ARCHITECTURE = $env:PLATFORM.substring($env:PLATFORM.length - 2)
artifacts:
- path: build\package\loot_api_python-$(GIT_DESCRIBE)-win32.7z
name: loot_api_python
- path: build\package\libloot-python-$(PACKAGE_VERSION)-python$(PYTHON_VERSION)-win$(ARCHITECTURE).zip
name: libloot-python
deploy:
- provider: BinTray
username: wrinklyninja
api_key:
secure: u234T2688DZmz4JH5+0iAHcUcL2fEIGculb8655bn4B1q7GckhplsitzgEbv3NFc
subject: wrinklyninja
repo: loot
package: loot-api-python
version: $(GIT_DESCRIBE)_$(APPVEYOR_REPO_BRANCH)
secure: PgsEA6TjHVf718zMnK7J/fT1hUAVNKBeWhpYgYaeCyeZk37VT4Ics6j7+B7ElLEr
subject: loot
repo: snapshots
package: libloot-python
version: $(PACKAGE_VERSION)
publish: true
artifact: loot_api_python
artifact: libloot-python
- provider: GitHub
tag: $(APPVEYOR_REPO_TAG_NAME)
release: libloot-python v$(APPVEYOR_REPO_TAG_NAME)
description: |
Requires Windows 7 or later and the [MSVC 2017 x86 redistributable](https://download.visualstudio.microsoft.com/download/pr/749aa419-f9e4-4578-a417-a43786af205e/d59197078cc425377be301faba7dd87a/vc_redist.x86.exe), and [7-Zip](http://www.7-zip.org/) to extract the archive.
auth_token:
secure: yDqT5l/e5MntbW99V6+MHlfFgNv+UIogFfeyUVqtFk5lFRB/dAraLLwKCLl6y+DH
artifact: libloot-python
draft: false
on:
appveyor_repo_tag: true
on_success:
- ps: python "$env:APPVEYOR_BUILD_FOLDER\delete_old_bintray_versions.py" -g loot/libloot-python -b loot/snapshots/libloot-python -u wrinklyninja -k $env:bintray_auth_token -t $env:github_auth_token -n 30
+5 -5
View File
@@ -1,13 +1,13 @@
LOOT API Python Wrapper
=======================
libloot-python
==============
This archive contains a build of the LOOT API Python wrapper module and its corresponding 32-bit Windows build of the LOOT API.
This archive contains a build of the libloot-python wrapper module and its corresponding 32-bit Windows build of libloot.
The DLL requires the [MSVC 2015 x86 redistributable](https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x86.exe)
The DLL requires the [MSVC 2017 x86 redistributable](https://download.visualstudio.microsoft.com/download/pr/749aa419-f9e4-4578-a417-a43786af205e/d59197078cc425377be301faba7dd87a/vc_redist.x86.exe)
to be installed.
See the [online documentation](http://loot-api-python.readthedocs.org/) for more information.
The library source code is available [on GitHub](https://github.com/loot/loot-api-python).
The library source code is available [on GitHub](https://github.com/loot/libloot-python).
---
+12 -10
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# LOOT API Python Module documentation build configuration file, created by
# libloot-python documentation build configuration file, created by
# sphinx-quickstart on Mon Aug 15 07:11:32 2016.
#
# This file is execfile()d with the current directory set to its
@@ -22,7 +22,7 @@
import os
import sys
sys.path.insert(0, os.path.abspath('../build/Release'))
sys.path.insert(0, os.path.abspath('../build/RelWithDebInfo'))
# -- General configuration ------------------------------------------------
@@ -34,6 +34,7 @@ sys.path.insert(0, os.path.abspath('../build/Release'))
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
]
@@ -54,8 +55,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'LOOT API Python Module'
copyright = u'2016, WrinklyNinja'
project = u'libloot-python'
copyright = u'2019, WrinklyNinja'
author = u'WrinklyNinja'
# The version info for the project you're documenting, acts as replacement for
@@ -139,7 +140,7 @@ html_theme = 'sphinx_rtd_theme'
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
#
# html_title = u'LOOT API Python Module v;atest'
# html_title = u'libloot-python v;atest'
# A shorter title for the navigation bar. Default is the same as html_title.
#
@@ -265,7 +266,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'LOOTAPIPythonModule.tex', u'LOOT API Python Module Documentation',
(master_doc, 'libloot-python.tex', u'libloot-python Documentation',
u'WrinklyNinja', 'manual'),
]
@@ -307,7 +308,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'lootapipythonmodule', u'LOOT API Python Module Documentation',
(master_doc, 'libloot-python', u'libloot-python Documentation',
[author], 1)
]
@@ -322,8 +323,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'LOOTAPIPythonModule', u'LOOT API Python Module Documentation',
author, 'LOOTAPIPythonModule', 'One line description of project.',
(master_doc, 'libloot-python', u'libloot-python Documentation',
author, 'libloot-python', 'One line description of project.',
'Miscellaneous'),
]
@@ -346,5 +347,6 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
'loot_api': ('http://loot.readthedocs.io/en/0.10.0/', None),
'loot': ('https://loot.readthedocs.io/en/0.15.0/', None),
'python': ('https://docs.python.org/3', None)
}
+3 -3
View File
@@ -1,10 +1,10 @@
.. LOOT API Python Module documentation master file, created by
.. libloot-python documentation master file, created by
sphinx-quickstart on Mon Aug 15 07:11:32 2016.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
LOOT API Python Module
======================
libloot-python
==============
Contents:
+12 -167
View File
@@ -2,172 +2,18 @@
API Reference
*************
As this API is just a wrapper for LOOT's C++ API, its documentation is linked to
for all non-Python-specific information.
As this API is just a wrapper for libloot's C++ API, its documentation is linked
to for all non-Python-specific information.
Enumerations
============
The wrapped enumeration types below are classes in Python, but the distinction
The wrapped enumeration types are classes in Python, but the distinction
makes no difference in practice, so they're grouped here for semantics. All
values are unsigned integer constants.
their values are unsigned integer constants.
.. py:class:: loot_api.GameType
.. automodule:: loot
:members:
:exclude-members: Version, WrapperVersion
Wraps :cpp:type:`loot::GameType` to expose the LOOT API's game
codes.
.. py:attribute:: fo3
.. py:attribute:: fo4
.. py:attribute:: fonv
.. py:attribute:: tes4
.. py:attribute:: tes5
.. py:class:: loot_api.LanguageCode
Wraps :cpp:type:`loot::LanguageCode` to expose the LOOT API's
language codes.
.. py:attribute:: english
.. py:attribute:: spanish
.. py:attribute:: russian
.. py:attribute:: french
.. py:attribute:: chinese
.. py:attribute:: polish
.. py:attribute:: brazilian_portuguese
.. py:attribute:: finnish
.. py:attribute:: german
.. py:attribute:: danish
.. py:attribute:: korean
.. py:class:: loot_api.MessageType
Wraps :cpp:type:`loot::MessageType` to expose the LOOT API's
message type codes.
.. py:attribute:: error
.. py:attribute:: say
.. py:attribute:: warn
.. py:class:: loot_api.PluginCleanliness
Wraps :cpp:type:`loot::PluginCleanliness` to expose the LOOT API's plugin cleanliness state codes.
.. py:attribute:: clean
.. py:attribute:: dirty
.. py:attribute:: do_not_clean
.. py:attribute:: unknown
Public-Field Data Structures
============================
Classes with public fields and no member functions.
.. py:class:: loot_api.MasterlistInfo
Wraps :cpp:class:`loot::MasterlistInfo`.
.. py:attribute:: revision_id
A Unicode string containing a Git commit's SHA-1 checksum.
.. py:attribute:: revision_date
A Unicode string containing the date of the commit given by :py:attr:`revision_id`, in ISO 8601 format (YYYY-MM-DD).
.. py:attribute:: is_modified
A boolean that is true if the masterlist has been modified from its state
at the commit given by :py:attr:`revision_id`.
.. py:class:: loot_api.Message
Wraps :cpp:class:`loot::SimpleMessage`.
.. py:attribute:: type
A :py:class:`loot_api.MessageType` giving the message type.
.. py:attribute:: language
A :py:class:`loot_api.LanguageCode` giving the message text language.
.. py:attribute:: text
A Unicode string containing the message text.
.. py:class:: loot_api.PluginTags
Wraps :cpp:class:`loot::PluginTags`.
.. py:attribute:: added
A set of Unicode strings giving Bash Tags suggested for addition.
.. py:attribute:: removed
A set of Unicode strings giving Bash Tags suggested for removal.
.. py:attribute:: userlist_modified
A boolean that is true if the suggestions contain metadata obtained from a loaded userlist.
Functions
=========
.. py:function:: loot_api.is_compatible(int, int, int) -> bool
Checks for API compatibility. Wraps :cpp:func:`loot::IsCompatible`.
.. py:function:: loot_api.create_database(game : loot_api.GameType, [game_path : unicode = u'', [game_local_path : unicode = u'']]) -> loot_api.DatabaseInterface
Initialise a new database handle. Wraps :cpp:func:`loot::IsCompatible`.
Classes
=======
.. py:class:: loot_api.DatabaseInterface
Wraps :cpp:class:`loot::DatabaseInterface`.
.. py:method:: eval_lists(loot_api.DatabaseInterface) -> NoneType
Evaluates all conditions and regular expression metadata entries in the loaded metadata lists. Wraps :cpp:func:`EvalLists`.
.. py:method:: get_masterlist_revision(loot_api.DatabaseInterface, unicode, bool) -> loot_api.MasterlistInfo
Gets the give masterlist’s source control revision. Wraps :cpp:func:`GetMasterlistRevision`.
.. py:method:: get_plugin_cleanliness(loot_api.DatabaseInterface, unicode) -> loot_api.PluginCleanliness
Determines the database’s knowledge of a plugin’s cleanliness. Wraps :cpp:func:`GetPluginCleanliness`.
.. py:method:: get_plugin_messages(loot_api.DatabaseInterface, unicode, loot_api.LanguageCode) -> list<loot_api.Message>
Outputs the messages associated with the given plugin in the database. Wraps :cpp:func:`GetPluginMessages`.
.. py:method:: get_plugin_tags(loot_api.DatabaseInterface, unicode) -> loot_api.PluginTags
Outputs the Bash Tags suggested for addition and removal by the database for the given plugin. Wraps :cpp:func:`GetPluginTags`.
.. py:method:: load_lists(loot_api.DatabaseInterface, masterlist_path : unicode, [userlist_path : unicode = u'']) -> NoneType
Loads the masterlist and userlist from the paths specified. Wraps :cpp:func:`LoadLists`.
.. py:method:: sort_plugins(loot_api.DatabaseInterface, list<unicode>) -> list<unicode>
Calculates a new load order for all a game’s installed plugins and outputs the sorted order. Wraps :cpp:func:`SortPlugins`.
.. py:method:: update_masterlist(loot_api.DatabaseInterface, unicode, unicode, unicode) -> bool
Updates the given masterlist using the given Git repository details. Wraps :cpp:func:`UpdateMasterlist`.
.. py:method:: write_minimal_list(loot_api.DatabaseInterface, unicode, bool) -> NoneType
Writes a minimal metadata file containing only Bash Tag suggestions and/or cleanliness info from the loaded metadata. Wraps :cpp:func:`WriteMinimalList`.
.. py:class:: loot_api.Version
.. py:class:: loot.Version
Wraps :cpp:class:`loot::LootVersion`.
@@ -187,14 +33,13 @@ Classes
A Unicode string containing the SHA-1 of the Git revision that the wrapped C++ API was built from.
.. py:staticmethod:: string() -> unicode
.. py:staticmethod:: string() -> str
Returns the API version as a string of the form ``major.minor.patch``
.. py:class:: loot_api.WrapperVersion
.. py:class:: loot.WrapperVersion
Provides information about the version of the LOOT API Python wrapper that is
being run.
Provides information about the version of libloot-python that is being run.
.. py:attribute:: major
@@ -212,6 +57,6 @@ Classes
A Unicode string containing the SHA-1 of the Git revision that the wrapped C++ API was built from.
.. py:staticmethod:: string() -> unicode
.. py:staticmethod:: string() -> str
Returns the API version as a string of the form ``major.minor.patch``
+10 -10
View File
@@ -7,16 +7,16 @@ Installing the wrapper
Build archives contain two binaries:
* ``loot_api.pyd`` is the Python wrapper
* ``loot_api.dll`` is the C++ API DLL that the Python wrapper was built against.
* ``loot.*.pyd`` is the Python wrapper
* ``loot.dll`` is the C++ library DLL that the Python wrapper was built against.
The C++ DLL requires the `Visual C++ 2015 Redistributable (x86)`_
The C++ DLL requires the `Visual C++ 2017 Redistributable (x86)`_
to be installed.
To use the wrapper, copy both files to wherever you want to import them from
(they must be in the same folder), and you're done!
.. _Visual C++ 2015 Redistributable (x86): https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x86.exe
.. _Visual C++ 2017 Redistributable (x86): https://download.visualstudio.microsoft.com/download/pr/749aa419-f9e4-4578-a417-a43786af205e/d59197078cc425377be301faba7dd87a/vc_redist.x86.exe
Using the wrapper
=================
@@ -27,10 +27,10 @@ Checking Compatibility
To check if the module loaded is compatible with the version of the API that you
developed against::
>>> import loot_api
>>> loot_api.is_compatible(0,10,0)
>>> import loot
>>> loot.is_compatible(0,14,0)
True
>>> loot_api.is_compatible(0,9,0)
>>> loot.is_compatible(0,9,0)
False
Getting a Plugin's Bash Tag Suggestions
@@ -38,9 +38,9 @@ Getting a Plugin's Bash Tag Suggestions
To get a plugin's Bash Tag suggestions from a ``masterlist.yaml`` metadata file::
>>> import loot_api
>>> db = loot_api.create_database(loot_api.GameType.tes4, '', '')
>>> db.load_lists('masterlist.yaml', '')
>>> import loot
>>> db = loot.create_game_handle(loot.GameType.tes4, 'C:\\path\\to\\oblivion\\directory')
>>> db.load_lists('masterlist.yaml')
>>> tags = db.get_plugin_tags(u'Unofficial Oblivion Patch.esp')
>>> tags.added
set([u'Scripts', u'Relations', u'C.Owner', u'Actors.AIPackages', u'Actors.Stats', u'Actors.ACBS', u'C.Music', u'Factions', u'Invent', u'Relev', u'Names', u'C.Light', u'Delev', u'C.Name', u'C.Climate', u'NPC.Class', u'Stats', u'Actors.DeathItem', u'Creatures.Blood', u'Actors.CombatStyle', u'Actors.AIData'])
+1
View File
@@ -0,0 +1 @@
from .loot import *
+89
View File
@@ -0,0 +1,89 @@
import itertools
import os
import platform
import subprocess
import sys
import tempfile
import threading
import time
from contextlib import contextmanager
from setuptools import Distribution, find_packages, setup
try: # python 3
import winreg
from urllib.request import urlretrieve
except ImportError: # python 2
import _winreg as winreg
from urllib import urlretrieve
MSVC_MIN_VERSION = (14, 0, 24215)
class BinaryDistribution(Distribution):
def has_ext_modules(foo):
return True
def is_msvc_redist_installed(major, minor, build):
if platform.machine().endswith("64"): # check if os is 64bit
sub_key = "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64"
else:
sub_key = "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86"
try:
key_handle = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, sub_key)
runtime_installed = winreg.QueryValueEx(key_handle, "Installed")[0]
installed_major = winreg.QueryValueEx(key_handle, "Major")[0]
installed_minor = winreg.QueryValueEx(key_handle, "Minor")[0]
installed_build = winreg.QueryValueEx(key_handle, "Bld")[0]
return (
runtime_installed != 0
and installed_major >= major
and installed_minor >= minor
and installed_build >= build
)
except WindowsError as exc:
return False
with open("README.md", "r") as fh:
long_description = fh.read()
def install_msvc_redist():
url = (
"https://download.microsoft.com/download/6/A/A/"
"6AA4EDFF-645B-48C5-81CC-ED5963AEAD48/vc_redist.x86.exe"
)
dl_dir = tempfile.mkdtemp()
dl_file = os.path.join(dl_dir, "vc_redist.exe")
urlretrieve(url, dl_file)
subprocess.call([dl_file, "/quiet"])
# trying to remove the temp folder triggers
# an error during py2 pip install
if not is_msvc_redist_installed(*MSVC_MIN_VERSION):
install_msvc_redist()
# The version string is substituted in by CMake.
setup(
name="libloot-python",
version="@LIBLOOT_PY_VERSION_MAJOR@.@LIBLOOT_PY_VERSION_MINOR@.@LIBLOOT_PY_VERSION_PATCH@",
description="A Python module that wraps libloot.",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/loot/libloot-python",
packages=find_packages(),
include_package_data=True,
package_data={"loot": ["loot*.pyd", "loot.dll"]},
distclass=BinaryDistribution,
classifiers=[
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: C++'
'Operating System :: Microsoft :: Windows',
'Intended Audience :: Developers',
],
python_requires='>=2.7',
)
-15
View File
@@ -1,15 +0,0 @@
{
"package": {
"name": "loot-api-python",
"repo": "loot",
"subject": "wrinklyninja"
},
"version": {
"name": "REPLACE_THIS_VERSION"
},
"files": [{
"includePattern": "build/package/(loot_api_python-.+\\.tar.xz)",
"uploadPattern": "$1"
}],
"publish": true
}
+83
View File
@@ -0,0 +1,83 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2014-2017 Oliver Hamlet
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<https://www.gnu.org/licenses/>.
*/
#include "convenience.h"
#include <loot/api.h>
namespace loot {
PluginTags GetPluginTags(const std::shared_ptr<DatabaseInterface> db, const std::string& plugin, bool evaluateConditions) {
PluginTags tags;
auto metadata = db->GetPluginMetadata(plugin, false, evaluateConditions);
if (metadata.has_value()) {
for (const auto &tag : metadata.value().GetTags()) {
if (tag.IsAddition())
tags.added.insert(tag.GetName());
else
tags.removed.insert(tag.GetName());
}
}
metadata = db->GetPluginUserMetadata(plugin, evaluateConditions);
if (metadata.has_value()) {
tags.userlist_modified = !metadata.value().GetTags().empty();
for (const auto &tag : metadata.value().GetTags()) {
if (tag.IsAddition())
tags.added.insert(tag.GetName());
else
tags.removed.insert(tag.GetName());
}
}
return tags;
}
PluginCleanliness GetPluginCleanliness(const std::shared_ptr<DatabaseInterface> db, const std::string& plugin, bool evaluateConditions) {
auto metadata = db->GetPluginMetadata(plugin, true, evaluateConditions);
if (!metadata.has_value()) {
return PluginCleanliness::unknown;
}
auto dirtyInfo = metadata.value().GetDirtyInfo();
auto cleanInfo = metadata.value().GetCleanInfo();
if (dirtyInfo.empty() == cleanInfo.empty()) {
return PluginCleanliness::unknown;
}
if (!cleanInfo.empty()) {
return PluginCleanliness::clean;
}
for (const auto& info : dirtyInfo) {
if (info.ChooseInfo("en").GetText().find("Do not clean") != std::string::npos) {
return PluginCleanliness::do_not_clean;
}
}
return PluginCleanliness::dirty;
}
}
+46
View File
@@ -0,0 +1,46 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2014-2017 Oliver Hamlet
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<https://www.gnu.org/licenses/>.
*/
#ifndef LIBLOOT_PYTHON_CONVENIENCE
#define LIBLOOT_PYTHON_CONVENIENCE
#include <memory>
#include <loot/enum/game_type.h>
#include <loot/database_interface.h>
#include "plugin_cleanliness.h"
#include "plugin_tags.h"
namespace loot {
PluginTags GetPluginTags(const std::shared_ptr<DatabaseInterface> db,
const std::string& plugin,
bool evaluateConditions = false);
PluginCleanliness GetPluginCleanliness(const std::shared_ptr<DatabaseInterface> db,
const std::string& plugin,
bool evaluateConditions = false);
}
#endif
+218 -80
View File
@@ -25,107 +25,245 @@
#include <loot/api.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include "convenience.h"
#include "wrapper_version.h"
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
using pybind11::arg;
using pybind11::enum_;
using pybind11::class_;
using pybind11::metaclass;
using std::filesystem::path;
using std::filesystem::u8path;
PYBIND11_PLUGIN(loot_api) {
using loot::CreateDatabase;
using loot::DatabaseInterface;
using loot::IsCompatible;
using loot::GameType;
using loot::LanguageCode;
using loot::LootVersion;
using loot::MessageType;
using loot::MasterlistInfo;
using loot::PluginCleanliness;
using loot::SimpleMessage;
using loot::PluginTags;
using loot::WrapperVersion;
namespace loot {
namespace py {
std::shared_ptr<GameInterface> CreateGameHandle(GameType gameType, std::string gamePath, std::string gameLocalPath) {
return CreateGameHandle(gameType, u8path(gamePath), u8path(gameLocalPath));
}
using pybind11::arg;
using pybind11::enum_;
using pybind11::class_;
void LoadLists(std::shared_ptr<DatabaseInterface> db, std::string masterlistPath, std::string userlistPath) {
return db->LoadLists(u8path(masterlistPath), u8path(userlistPath));
}
const std::string emptyString;
void WriteMinimalList(std::shared_ptr<DatabaseInterface> db, std::string outputFile, bool overwrite) {
return db->WriteMinimalList(u8path(outputFile), overwrite);
}
pybind11::module module("loot_api", "A Python module that wraps the LOOT API, generated by pybind11.");
bool UpdateMasterlist(std::shared_ptr<DatabaseInterface> db, std::string masterlistPath, std::string remoteUrl, std::string remoteBranch) {
return db->UpdateMasterlist(u8path(masterlistPath), remoteUrl, remoteBranch);
}
enum_<GameType>(module, "GameType")
MasterlistInfo GetMasterlistRevision(std::shared_ptr<DatabaseInterface> db, std::string masterlistPath, bool getShortId) {
return db->GetMasterlistRevision(u8path(masterlistPath), getShortId);
}
void SetLoggingCallback(std::function<void(LogLevel, const char*)> callback) {
callback = [callback](LogLevel level, const char* message) {
pybind11::gil_scoped_acquire acquire;
callback(level, message);
};
loot::SetLoggingCallback(callback);
}
}
void bindEnums(pybind11::module& module) {
pybind11::options options;
options.disable_function_signatures();
enum_<GameType>(module, "GameType", "Wraps :cpp:enum:`loot::GameType` to expose libloot's game codes.")
.value("tes4", GameType::tes4)
.value("tes5", GameType::tes5)
.value("tes5se", GameType::tes5se)
.value("tes5vr", GameType::tes5vr)
.value("fo3", GameType::fo3)
.value("fonv", GameType::fonv)
.value("fo4", GameType::fo4);
.value("fo4", GameType::fo4)
.value("fo4vr", GameType::fo4vr);
enum_<LanguageCode>(module, "LanguageCode")
.value("english", LanguageCode::english)
.value("spanish", LanguageCode::spanish)
.value("russian", LanguageCode::russian)
.value("french", LanguageCode::french)
.value("chinese", LanguageCode::chinese)
.value("polish", LanguageCode::polish)
.value("brazilian_portuguese", LanguageCode::brazilian_portuguese)
.value("finnish", LanguageCode::finnish)
.value("german", LanguageCode::german)
.value("danish", LanguageCode::danish)
.value("korean", LanguageCode::korean);
enum_<LogLevel>(module, "LogLevel", "Wraps :cpp:enum:`loot::LogLevel` to expose libloot's log level codes.")
.value("trace", LogLevel::trace)
.value("debug", LogLevel::debug)
.value("info", LogLevel::info)
.value("warning", LogLevel::warning)
.value("error", LogLevel::error)
.value("fatal", LogLevel::fatal);
enum_<MessageType>(module, "MessageType")
enum_<MessageType>(module, "MessageType", "Wraps :cpp:enum:`loot::MessageType` to expose libloot's message type codes.")
.value("say", MessageType::say)
.value("warn", MessageType::warn)
.value("error", MessageType::error);
enum_<PluginCleanliness>(module, "PluginCleanliness")
.value("clean", PluginCleanliness::clean)
.value("dirty", PluginCleanliness::dirty)
.value("do_not_clean", PluginCleanliness::do_not_clean)
.value("unknown", PluginCleanliness::unknown);
enum_<PluginCleanliness>(module, "PluginCleanliness", "Codes used to indicate the cleanliness of a plugin according to the information contained within the loaded masterlist / userlist.")
.value("clean", PluginCleanliness::clean, "Indicates that the plugin is clean.")
.value("dirty", PluginCleanliness::dirty, "Indicates that the plugin is dirty.")
.value("do_not_clean", PluginCleanliness::do_not_clean, "Indicates that the plugin contains dirty edits, but that they are part of the plugin's intended functionality and should not be removed.")
.value("unknown", PluginCleanliness::unknown, "Indicates that no data is available on whether the plugin is dirty or not.");
}
class_<MasterlistInfo>(module, "MasterlistInfo")
.def_readwrite("revision_id", &MasterlistInfo::revision_id)
.def_readwrite("revision_date", &MasterlistInfo::revision_date)
.def_readwrite("is_modified", &MasterlistInfo::is_modified);
void bindMetadataClasses(pybind11::module& module) {
class_<MasterlistInfo>(module, "MasterlistInfo", "Wraps :cpp:class:`loot::MasterlistInfo`.")
.def_readwrite("revision_id", &MasterlistInfo::revision_id, "A Unicode string containing a Git commit's SHA-1 checksum.")
.def_readwrite("revision_date", &MasterlistInfo::revision_date, "A Unicode string containing the date of the commit given by :py:attr:`~loot.MasterlistInfo.revision_id`, in ISO 8601 format (YYYY-MM-DD).")
.def_readwrite("is_modified", &MasterlistInfo::is_modified, "A boolean that is true if the masterlist has been modified from its state at the commit given by :py:attr:`~loot.MasterlistInfo.revision_id`.");
class_<SimpleMessage>(module, "Message")
.def_readwrite("type", &SimpleMessage::type)
.def_readwrite("language", &SimpleMessage::language)
.def_readwrite("text", &SimpleMessage::text);
class_<SimpleMessage>(module, "SimpleMessage", "Wraps :cpp:class:`loot::SimpleMessage`.")
.def_readwrite("type", &SimpleMessage::type, "A :py:class:`loot.MessageType` giving the message type.")
.def_readwrite("language", &SimpleMessage::language, "A Unicode string giving the message text language.")
.def_readwrite("text", &SimpleMessage::text, "A Unicode string containing the message text.")
.def_readwrite("condition", &SimpleMessage::condition, "A Unicode string containing the message condition.");
class_<PluginTags>(module, "PluginTags")
.def_readwrite("added", &PluginTags::added)
.def_readwrite("removed", &PluginTags::removed)
.def_readwrite("userlist_modified", &PluginTags::userlist_modified);
.def_readwrite("added", &PluginTags::added, "A set of Unicode strings giving Bash Tags suggested for addition.")
.def_readwrite("removed", &PluginTags::removed, "A set of Unicode strings giving Bash Tags suggested for removal.")
.def_readwrite("userlist_modified", &PluginTags::userlist_modified, "A boolean that is true if the suggestions contain metadata obtained from a loaded userlist.");
class_<LootVersion>(module, "Version")
.def_readonly_static("major", &LootVersion::major)
.def_readonly_static("minor", &LootVersion::minor)
.def_readonly_static("patch", &LootVersion::patch)
.def_readonly_static("revision", &LootVersion::revision)
.def_static("string", LootVersion::string);
class_<WrapperVersion>(module, "WrapperVersion")
.def_readonly_static("major", &WrapperVersion::major)
.def_readonly_static("minor", &WrapperVersion::minor)
.def_readonly_static("patch", &WrapperVersion::patch)
.def_readonly_static("revision", &WrapperVersion::revision)
.def_static("string", WrapperVersion::string);
class_<DatabaseInterface, std::shared_ptr<DatabaseInterface>>(module, "DatabaseInterface")
.def("load_lists", &DatabaseInterface::LoadLists, arg("masterlist_path"), arg("userlist_path") = emptyString)
.def("eval_lists", &DatabaseInterface::EvalLists)
.def("sort_plugins", &DatabaseInterface::SortPlugins)
.def("update_masterlist", &DatabaseInterface::UpdateMasterlist)
.def("get_masterlist_revision", &DatabaseInterface::GetMasterlistRevision)
.def("get_plugin_tags", &DatabaseInterface::GetPluginTags)
.def("get_plugin_messages", &DatabaseInterface::GetPluginMessages)
.def("get_plugin_cleanliness", &DatabaseInterface::GetPluginCleanliness)
.def("write_minimal_list", &DatabaseInterface::WriteMinimalList);
module.def("is_compatible", &IsCompatible);
module.def("create_database", &CreateDatabase, arg("game"), arg("game_path") = emptyString, arg("game_local_path") = emptyString);
return module.ptr();
class_<PluginMetadata>(module, "PluginMetadata", "Wraps :cpp:class:`loot::PluginMetadata`.")
.def("get_simple_messages",
&PluginMetadata::GetSimpleMessages,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Get the plugin's messages as SimpleMessage objects for the given language. Wraps :cpp:func:`GetSimpleMessages`.");
}
void bindVersionClasses(pybind11::module& module) {
// FIXME: For some reason the static properties have their docstrings ignored.
class_<LootVersion>(module, "Version", "Wraps :cpp:class:`loot::LootVersion`.")
.def_readonly_static("major", &LootVersion::major, "An unsigned integer giving the major version number. Read-only.")
.def_readonly_static("minor", &LootVersion::minor, "An unsigned integer giving the minor version number. Read-only.")
.def_readonly_static("patch", &LootVersion::patch, "An unsigned integer giving the patch version number. Read-only.")
.def_readonly_static("revision", &LootVersion::revision, "A Unicode string containing the Git commit hash that the wrapped libloot was built from.")
.def_static("string",
LootVersion::GetVersionString,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Returns the libloot version as a string of the form ``major.minor.patch``.");
class_<WrapperVersion>(module, "WrapperVersion", "Provides information about the version of libloot-python that is being run.")
.def_readonly_static("major", &WrapperVersion::major, "An unsigned integer giving the major version number. Read-only.")
.def_readonly_static("minor", &WrapperVersion::minor, "An unsigned integer giving the minor version number. Read-only.")
.def_readonly_static("patch", &WrapperVersion::patch, "An unsigned integer giving the patch version number. Read-only.")
.def_readonly_static("revision", &WrapperVersion::revision, "A Unicode string containing the Git commit hash that the Python module was built from.")
.def_static("string",
WrapperVersion::string,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Returns the module version as a string of the form ``major.minor.patch``.");
}
void bindInterfaceClasses(pybind11::module& module) {
class_<GameInterface, std::shared_ptr<GameInterface>>(module, "GameInterface", "Wraps :cpp:class:`loot::GameInterface`.")
.def("load_current_load_order_state",
&GameInterface::LoadCurrentLoadOrderState,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Load the current load order state, discarding any previously held state. Wraps :cpp:func:`LoadCurrentLoadOrderState`.")
.def("get_database",
&GameInterface::GetDatabase,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Get a database handle. Wraps :cpp:func:`GetDatabase`.")
.def("load_plugins",
&GameInterface::LoadPlugins,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Load the given plugins. Wraps :cpp:func:`LoadPlugins`.")
.def("get_plugin",
&GameInterface::GetPlugin,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Get the given loaded plugin. Wraps :cpp:func:`GetPlugin`.");
class_<DatabaseInterface, std::shared_ptr<DatabaseInterface>>(module, "DatabaseInterface", "Wraps :cpp:class:`loot::DatabaseInterface`.")
.def("load_lists",
&py::LoadLists,
arg("masterlist_path"),
arg("userlist_path") = "",
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Loads the masterlist and userlist from the paths specified. Wraps :cpp:func:`LoadLists`.")
.def("update_masterlist",
&py::UpdateMasterlist,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Updates the given masterlist using the given Git repository details. Wraps :cpp:func:`UpdateMasterlist`.")
.def("get_masterlist_revision",
&py::GetMasterlistRevision,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Gets the give masterlist's source control revision. Wraps :cpp:func:`GetMasterlistRevision`.")
.def("get_plugin_metadata",
&DatabaseInterface::GetPluginMetadata,
arg("plugin"),
arg("includeUserMetadata") = true,
arg("evaluateConditions") = false,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Get all a plugin's loaded metadata. Wraps :cpp:func:`GetPluginMetadata`.")
.def("get_plugin_tags",
&GetPluginTags,
arg("plugin"),
arg("evaluateConditions") = false,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Outputs the Bash Tags suggested for addition and removal by the database for the given plugin.")
.def("get_plugin_cleanliness",
&GetPluginCleanliness,
arg("plugin"),
arg("evaluateConditions") = false,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Determines the database's knowledge of a plugin's cleanliness. Outputs whether the plugin should be cleaned or not, or if no data is available.")
.def("write_minimal_list",
&py::WriteMinimalList,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Writes a minimal metadata file containing only Bash Tag suggestions and/or cleanliness info from the loaded metadata. Wraps :cpp:func:`WriteMinimalList`.");
class_<PluginInterface, std::shared_ptr<PluginInterface>>(module, "PluginInterface")
.def_property_readonly("name",
&PluginInterface::GetName,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"The plugin's name. Read-only. Wraps :cpp:func:`GetName`.")
.def("is_master",
&PluginInterface::IsMaster,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Check if the plugin is a master. Wraps :cpp:func:`IsMaster`.")
.def("is_light_master",
&PluginInterface::IsLightMaster,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Check if the plugin is a light master. Wraps :cpp:func:`IsLightMaster`.")
.def("is_valid_as_light_master",
&PluginInterface::IsValidAsLightMaster,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Check if the plugin contains only records with FormIDs that are valid in a light master. Wraps :cpp:func:`IsValidAsLightMaster`.");
}
void bindClasses(pybind11::module& module) {
bindMetadataClasses(module);
bindVersionClasses(module);
bindInterfaceClasses(module);
}
void bindFunctions(pybind11::module& module) {
module.def("set_logging_callback",
&py::SetLoggingCallback,
arg("callback"),
"Set the callback function that is called when logging. Wraps :cpp:func:`loot::SetLoggingCallback`.");
// Need to clear the stored logging callback when exiting, or Python will
// hang because the callback pointer is still stored by libloot.
auto atexit = pybind11::module::import("atexit");
atexit.attr("register")(pybind11::cpp_function([]() {
SetLoggingCallback(nullptr);
}));
module.def("is_compatible",
&IsCompatible,
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Checks for API compatibility. Wraps :cpp:func:`loot::IsCompatible`.");
module.def("create_game_handle",
&py::CreateGameHandle,
arg("game"),
arg("game_path"),
arg("game_local_path") = "",
pybind11::call_guard<pybind11::gil_scoped_release>(),
"Initialise a new game handle. Wraps :cpp:func:`loot::CreateGameHandle`.");
}
}
PYBIND11_MODULE(loot, module) {
module.doc() = "A Python module that wraps libloot, generated by pybind11.";
loot::bindEnums(module);
loot::bindClasses(module);
loot::bindFunctions(module);
}
+52
View File
@@ -0,0 +1,52 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2016 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<https://www.gnu.org/licenses/>.
*/
#ifndef LIBLOOT_PYTHON_PLUGIN_CLEANLINESS
#define LIBLOOT_PYTHON_PLUGIN_CLEANLINESS
/**
* The namespace used by libloot.
*/
namespace loot {
/**
* @brief Codes used to indicate the cleanliness of a plugin according to the
* information contained within the loaded masterlist/userlist.
*/
enum struct PluginCleanliness : unsigned int {
/** Indicates that the plugin is clean. */
clean,
/** Indicates that the plugin is dirty. */
dirty,
/**
* Indicates that the plugin contains dirty edits, but that they are
* part of the plugin's intended functionality and should not be removed.
*/
do_not_clean,
/**
* Indicates that no data is available on whether the plugin is dirty or not.
*/
unknown,
};
}
#endif
+58
View File
@@ -0,0 +1,58 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2016 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<https://www.gnu.org/licenses/>.
*/
#ifndef LIBLOOT_PYTHON_PLUGIN_TAGS
#define LIBLOOT_PYTHON_PLUGIN_TAGS
#include <set>
#include <string>
namespace loot {
/**
* @brief A structure that holds data about the Bash Tag suggestions made by
* LOOT for a plugin.
*/
struct PluginTags {
inline PluginTags() : userlist_modified(false) {}
/**
* @brief A set of Bash Tag names suggested for addition to the specified
* plugin. Empty if no Bash Tag additions are suggested.
*/
std::set<std::string> added;
/**
* @brief A set of Bash Tag names suggested for removal from the specified
* plugin. Empty if no Bash Tag removals are suggested.
*/
std::set<std::string> removed;
/**
* @brief `true` if the Bash Tag suggestions were modified by data in the
* userlist, `false` otherwise.
*/
bool userlist_modified;
};
}
#endif
-118
View File
@@ -1,118 +0,0 @@
#!/usr/bin/env python
import cProfile
import os.path
import shutil
import unittest
from loot_api import Version
from loot_api import WrapperVersion
from loot_api import GameType
from loot_api import LanguageCode
from loot_api import Message
from loot_api import MessageType
from loot_api import create_database
from loot_api import is_compatible
class GameFixture(unittest.TestCase):
game_path = os.path.join(u'.', u'Oblivion')
local_path = os.path.join(u'.', u'local')
def setUp(self):
data_path = os.path.join(self.game_path, 'Data')
master_file = os.path.join(data_path, 'Oblivion.esm')
os.makedirs(data_path)
open(master_file, 'a').close()
os.makedirs(self.local_path)
def tearDown(self):
shutil.rmtree(self.game_path)
shutil.rmtree(self.local_path)
class TestLootApi(GameFixture):
def test_is_compatible(self):
self.assertFalse(is_compatible(0, 9, 0))
self.assertTrue(is_compatible(0, 10, 0))
def test_version(self):
self.assertEqual(Version.major, 0)
self.assertEqual(Version.minor, 10)
self.assertEqual(Version.patch, 0)
self.assertNotEqual(Version.revision, u'')
self.assertEqual(Version.string(), "0.10.0")
def test_wrapper_version(self):
self.assertEqual(WrapperVersion.major, 1)
self.assertEqual(WrapperVersion.minor, 1)
self.assertEqual(WrapperVersion.patch, 0)
self.assertNotEqual(WrapperVersion.revision, u'')
self.assertNotEqual(WrapperVersion.revision, Version.revision)
self.assertEqual(WrapperVersion.string(), "1.1.0")
def test_create_db(self):
db = create_database(GameType.tes4, self.game_path, self.local_path)
self.assertNotEqual(db, None)
class TestDatabaseInterface(GameFixture):
masterlist_path = u'masterlist.yaml'
def setUp(self):
super(TestDatabaseInterface, self).setUp()
self.db = create_database(GameType.tes4, self.game_path, self.local_path)
def test_load_lists(self):
self.db.load_lists(self.masterlist_path, u'')
def test_get_plugin_tags(self):
self.db.load_lists(self.masterlist_path, u'')
tags = self.db.get_plugin_tags(u'Unofficial Oblivion Patch.esp')
self.assertNotEqual(tags, None)
self.assertFalse(tags.userlist_modified)
self.assertEqual(tags.added, set([
u'Actors.ACBS',
u'Actors.AIData',
u'Actors.AIPackages',
u'Actors.CombatStyle',
u'Actors.DeathItem',
u'Actors.Stats',
u'C.Climate',
u'C.Light',
u'C.Music',
u'C.Name',
u'C.Owner',
u'Creatures.Blood',
u'Delev',
u'Factions',
u'Invent',
u'Names',
u'NPC.Class',
u'Relations',
u'Relev',
u'Scripts',
u'Stats'
]))
self.assertEqual(tags.removed, set([u'C.Water']))
# cProfile.runctx('self.test_get_plugin_tags_performance()', globals(), locals(), sort='cumtime')
# def test_get_plugin_tags_performance(self):
# self.db.load_lists(self.masterlist_path, u'')
#
# for i in xrange(200000):
# tags = self.db.get_plugin_tags(u'Unofficial Oblivion Patch.esp')
def test_get_plugin_messages(self):
self.db.load_lists(self.masterlist_path, u'')
messages = self.db.get_plugin_messages(u'Oblivion.esm', LanguageCode.english)
self.assertEqual(len(messages), 1)
self.assertEqual(messages[0].type, MessageType.error)
self.assertEqual(messages[0].text, u'This must not be activated. However, it can be useful when porting Oblivion mods to Nehrim.')
if __name__ == '__main__':
unittest.main()
+3 -3
View File
@@ -25,9 +25,9 @@
#include "wrapper_version.h"
namespace loot {
const unsigned int WrapperVersion::major = 1;
const unsigned int WrapperVersion::minor = 1;
const unsigned int WrapperVersion::patch = 0;
const unsigned int WrapperVersion::major = @LIBLOOT_PY_VERSION_MAJOR@;
const unsigned int WrapperVersion::minor = @LIBLOOT_PY_VERSION_MINOR@;
const unsigned int WrapperVersion::patch = @LIBLOOT_PY_VERSION_PATCH@;
const std::string WrapperVersion::revision = "@GIT_COMMIT_STRING@";
std::string WrapperVersion::string() {

Some files were not shown because too many files have changed in this diff Show More