58 Commits
Author SHA1 Message Date
Oliver Hamlet 88cbaa58af Describe this repo as unmaintained in the readme
The libloot repo's Python wrapper is experimental and may not be kept, so link to a specific commit that has it and word the message to be true even if the wrapper is removed in the future.
2025-06-12 17:16:57 +01:00
Oliver Hamlet 900d5132ce Update actions/upload-artifact to v4 2025-04-29 22:53:34 +01:00
Oliver Hamlet 929d3ac659 Update Windows CI to windows-2025
windows-2019 will be fully retired by the end of June, and 2025 is the latest version.

This means that Windows build artifacts will be built using MSVC 2022, not MSVC 2019. They should be ABI-compatible across this change, since they expose no third-party types and Microsoft say that their compiler and standard library didn't break ABI between 2019 and 2022, but a newer version of the redistributable might be needed.
2025-04-29 22:40:08 +01:00
Oliver Hamlet ddc40b70e9 Replace use of set-output in GitHub Actions workflows
It's deprecated and will be disabled at some point.
2023-06-05 17:52:05 +01:00
Oliver Hamlet 43ba191ac5 Update GitHub Actions
To resolve deprecation warnings in workflows.
2023-06-05 17:49:53 +01:00
Oliver Hamlet 64293324c3 Stop uploading snapshot artifacts to Artifactory
The JFrog account that's used by the LOOT organisation will be
deactivated on 2023-07-02 because JFrog are sunsetting their free
accounts.
2023-06-05 17:34:29 +01:00
Oliver Hamlet 5a9179be1b Update pybind11 to 2.9.2
pybind11 2.6.0 increased the minimum required version of CMake to 3.4.
2022-07-02 17:00:47 +01:00
Oliver Hamlet 6f6b2269cf Migrate to windows-2019 GitHub Actions runners
windows-2016 was scheduled for removal in April 2022, though the
previous commit's build still worked.

This also means that the MSVC 2019 redistributable is now required.
Although libloot 0.15 is built with MSVC 2017, 2019 can link code
build with 2017 and the 2019 redistributable effectively replaces
the 2017 redistributable, so there's no compatibility issues.
2022-07-02 16:49:52 +01:00
Oliver Hamlet 024ab1f1e5 Drop support for Python 2.7
It's been end of life since January 2020.
2022-07-02 16:36:14 +01:00
Oliver Hamlet a7d26204a1 Replace Bintray with Artifactory
JFrog are closing down the former tomorrow.
2021-04-30 22:37:45 +01:00
Oliver Hamlet 8247ef9f62 Migrate CI to GitHub Actions 2021-02-14 15:19:12 +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
22 changed files with 617 additions and 271 deletions
+88
View File
@@ -0,0 +1,88 @@
name: CI
on:
push:
# Don't run this workflow when a tag is pushed.
branches:
- '*'
pull_request:
env:
MSVC_CONFIG: RelWithDebInfo
jobs:
windows:
runs-on: windows-2025
strategy:
matrix:
platform: [Win32, x64]
python-version: [3.7]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get Python architecture
id: get-python-architecture
shell: bash
run: |
if [[ "${{ matrix.platform }}" == "Win32" ]]
then
PLATFORM=x86
else
PLATFORM=x64
fi
echo "architecture=$PLATFORM" >> $GITHUB_OUTPUT
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ steps.get-python-architecture.outputs.architecture }}
- name: Get descriptive version
id: get-version
shell: bash
run: |
GIT_DESCRIBE=$(git describe --tags --long --abbrev=7)
DESC_REF=${GIT_DESCRIBE}_${GITHUB_REF#refs/*/}
SAFE_DESC_REF=${DESC_REF//[\/<>\"|]/_}
echo "version=$SAFE_DESC_REF" >> $GITHUB_OUTPUT
- name: Run CMake
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -A ${{ matrix.platform }} -DCPACK_PACKAGE_VERSION="${{ steps.get-version.outputs.version }}-python${{ matrix.python-version }}"
cmake --build . --config ${{ env.MSVC_CONFIG }}
- name: Run tests
run: |
cd build
ctest -C ${{ env.MSVC_CONFIG }}
- name: Build archive
id: build-archive
shell: bash
run: |
cd build
cpack -C ${{ env.MSVC_CONFIG }}
VERSION="${{ steps.get-version.outputs.version }}-python${{ matrix.python-version }}"
if [[ "${{ matrix.platform }}" == "Win32" ]]
then
PLATFORM=win32
else
PLATFORM=win64
fi
echo "filename=libloot-python-${VERSION}-${PLATFORM}.zip" >> $GITHUB_OUTPUT
- name: Upload archive
uses: actions/upload-artifact@v4
with:
name: ${{ steps.build-archive.outputs.filename }}
path: build/package/${{ steps.build-archive.outputs.filename }}
if: github.event_name == 'push'
+92
View File
@@ -0,0 +1,92 @@
name: Release
on:
push:
tags: '*'
env:
MSVC_CONFIG: RelWithDebInfo
jobs:
create_release:
runs-on: ubuntu-18.04
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
git_tag: ${{ steps.get-git-tag.outputs.name }}
steps:
- name: Get Git tag
id: get-git-tag
run: echo "name=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get-git-tag.outputs.name }}
release_name: libloot-python v${{ steps.get-git-tag.outputs.name }}
body: |
Requires Windows 7 or later and the [MSVC 2022 x86 redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe), and [7-Zip](http://www.7-zip.org/) to extract the archive.
windows:
runs-on: windows-2025
needs: create_release
strategy:
matrix:
platform: [Win32, x64]
python-version: [3.7]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Get descriptive version
id: get-version
shell: bash
run: |
GIT_DESCRIBE=$(git describe --tags --long --abbrev=7)
DESC_REF=${GIT_DESCRIBE}_${GITHUB_REF#refs/*/}
SAFE_DESC_REF=${DESC_REF//[\/<>\"|]/_}
echo "version=$SAFE_DESC_REF" >> $GITHUB_OUTPUT
- name: Run CMake
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -A ${{ matrix.platform }} -DCPACK_PACKAGE_VERSION="${{ steps.get-version.outputs.version }}-python${{ matrix.python-version }}"
cmake --build . --config ${{ env.MSVC_CONFIG }}
- name: Build archive
id: build-archive
shell: bash
run: |
cd build
cpack -C ${{ env.MSVC_CONFIG }}
VERSION="${{ steps.get-version.outputs.version }}-python${{ matrix.python-version }}"
if [[ "${{ matrix.platform }}" == "Win32" ]]
then
PLATFORM=win32
else
PLATFORM=win64
fi
echo "filename=libloot-python-${VERSION}-${PLATFORM}.zip" >> $GITHUB_OUTPUT
- name: Upload Archive
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create_release.outputs.upload_url }}
asset_path: build/package/${{ steps.build-archive.outputs.filename }}
asset_name: ${{ steps.build-archive.outputs.filename }}
asset_content_type: application/x-7z-compressed
+2
View File
@@ -50,3 +50,5 @@ $RECYCLE.BIN/
# Windows shortcuts # Windows shortcuts
*.lnk *.lnk
.vscode/
+70 -62
View File
@@ -1,15 +1,23 @@
cmake_minimum_required (VERSION 2.8.12) cmake_minimum_required (VERSION 3.4)
project (loot_api_python) project (libloot-python)
include(ExternalProject) include(ExternalProject)
set(EXTERNAL_PROJECTS_PATH "${CMAKE_BINARY_DIR}/external/src") set(EXTERNAL_PROJECTS_PATH "${CMAKE_BINARY_DIR}/external/src")
make_directory(${EXTERNAL_PROJECTS_PATH}) 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 # Get Build Revision
############################## ##############################
set(LIBLOOT_PY_VERSION_MAJOR 4)
set(LIBLOOT_PY_VERSION_MINOR 0)
set(LIBLOOT_PY_VERSION_PATCH 2)
find_package(Git) find_package(Git)
IF (GIT_FOUND) IF (GIT_FOUND)
@@ -21,16 +29,18 @@ ELSE()
SET (GIT_COMMIT_STRING "unknown") SET (GIT_COMMIT_STRING "unknown")
ENDIF () 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}") message(STATUS "Git revision: ${GIT_COMMIT_STRING}")
# Write to file. # 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}/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 # pybind11
####################################### #######################################
set(PYBIND11_VERSION "2.2.1") set(PYBIND11_VERSION "2.9.2")
set(PYBIND11_URL "https://github.com/pybind/pybind11/archive/v${PYBIND11_VERSION}.tar.gz") 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_DOWNLOAD_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-${PYBIND11_VERSION}.tar.gz")
set(PYBIND11_EXTRACTED_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-${PYBIND11_VERSION}") set(PYBIND11_EXTRACTED_PATH "${EXTERNAL_PROJECTS_PATH}/pybind11-${PYBIND11_VERSION}")
@@ -46,55 +56,52 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz ${PYBIND11_DOWNLOAD_PATH}
add_subdirectory(${PYBIND11_EXTRACTED_PATH}) add_subdirectory(${PYBIND11_EXTRACTED_PATH})
####################################### #######################################
# LOOT API # libloot
####################################### #######################################
if (CMAKE_SYSTEM_NAME MATCHES "Windows") if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(LOOT_API_URL "https://github.com/loot/loot-api/releases/download/0.12.0/loot_api-0.12.0-0-g42cacca_dev-win32.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() else()
set(LOOT_API_URL "https://github.com/loot/loot-api/releases/download/0.12.0/loot-api.tar.xz") 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(LIBLOOT_URL "https://github.com/loot/libloot/releases/download/0.15.0/libloot.tar.xz")
endif() endif()
ExternalProject_Add(loot-api-c++ ExternalProject_Add(libloot
PREFIX "external" PREFIX "external"
URL ${LOOT_API_URL} URL ${LIBLOOT_URL}
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "") INSTALL_COMMAND "")
ExternalProject_Get_Property(loot-api-c++ SOURCE_DIR) ExternalProject_Get_Property(libloot SOURCE_DIR)
set(LOOT_API_EXTRACTED_PATH ${SOURCE_DIR}) set(LIBLOOT_EXTRACTED_PATH ${SOURCE_DIR})
include_directories("${CMAKE_SOURCE_DIR}/src" include_directories("${CMAKE_SOURCE_DIR}/src"
"${LOOT_API_EXTRACTED_PATH}/include") "${LIBLOOT_EXTRACTED_PATH}/include")
link_directories(${LOOT_API_EXTRACTED_PATH}) link_directories(${LIBLOOT_EXTRACTED_PATH})
set(LOOT_API_STATIC_LIBRARY "${CMAKE_STATIC_LIBRARY_PREFIX}loot_api${CMAKE_STATIC_LIBRARY_SUFFIX}") set(LIBLOOT_STATIC_LIBRARY "${CMAKE_STATIC_LIBRARY_PREFIX}loot${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(LOOT_API_SHARED_LIBRARY "${CMAKE_SHARED_LIBRARY_PREFIX}loot_api${CMAKE_SHARED_LIBRARY_SUFFIX}") set(LIBLOOT_SHARED_LIBRARY "${CMAKE_SHARED_LIBRARY_PREFIX}loot${CMAKE_SHARED_LIBRARY_SUFFIX}")
#######################################
# Test Masterlist
#######################################
ExternalProject_Add(test-masterlist
PREFIX "external"
URL "https://github.com/loot/oblivion/archive/09286601739c11a3a3e4552c6ddbdcaeffd1a8d3.zip"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
####################################### #######################################
# Python Module # Python Module
####################################### #######################################
pybind11_add_module(loot_api "${CMAKE_SOURCE_DIR}/src/main.cpp" pybind11_add_module(libloot-python "${CMAKE_SOURCE_DIR}/src/main.cpp"
"${CMAKE_SOURCE_DIR}/src/convenience.cpp" "${CMAKE_SOURCE_DIR}/src/convenience.cpp"
"${CMAKE_BINARY_DIR}/generated/wrapper_version.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)
if (CMAKE_SYSTEM_NAME MATCHES "Windows") 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() else()
target_link_libraries(loot_api PRIVATE ${LOOT_API_SHARED_LIBRARY}) target_link_libraries(libloot-python PRIVATE ${LIBLOOT_SHARED_LIBRARY})
endif() endif()
######################################## ########################################
@@ -103,37 +110,44 @@ endif()
find_package(PythonInterp REQUIRED) find_package(PythonInterp REQUIRED)
# Copy the API binary to the build directory. # Copy the libloot binary to the build directory.
add_custom_command(TARGET loot_api POST_BUILD add_custom_command(TARGET libloot-python POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${LOOT_API_EXTRACTED_PATH}/${LOOT_API_SHARED_LIBRARY}" "${LIBLOOT_EXTRACTED_PATH}/${LIBLOOT_SHARED_LIBRARY}"
"$<TARGET_FILE_DIR:loot_api>/${LOOT_API_SHARED_LIBRARY}") "$<TARGET_FILE_DIR:libloot-python>/${LIBLOOT_SHARED_LIBRARY}")
# Copy the test masterlist to the build directory. enable_testing()
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")
# Also copy the test Python script to the build directory. add_test(NAME python
add_custom_command(TARGET loot_api POST_BUILD WORKING_DIRECTORY $<TARGET_FILE_DIR:libloot-python>
COMMAND ${CMAKE_COMMAND} -E copy_if_different COMMAND "${PYTHON_EXECUTABLE}" ${CMAKE_SOURCE_DIR}/test/test.py)
"${CMAKE_SOURCE_DIR}/src/test.py"
"$<TARGET_FILE_DIR:loot_api>/test.py")
######################################## ########################################
# Install # Install
######################################## ########################################
install(TARGETS loot_api install(TARGETS libloot-python
DESTINATION "." DESTINATION "loot"
CONFIGURATIONS Release) CONFIGURATIONS Release RelWithDebInfo)
install(FILES "${LOOT_API_EXTRACTED_PATH}/${LOOT_API_SHARED_LIBRARY}" install(FILES "${LIBLOOT_EXTRACTED_PATH}/${LIBLOOT_SHARED_LIBRARY}"
"${CMAKE_SOURCE_DIR}/docs/README.md" "${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 "." 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 # CPack
@@ -142,28 +156,22 @@ install(FILES "${LOOT_API_EXTRACTED_PATH}/${LOOT_API_SHARED_LIBRARY}"
# Get version info using Git if available # Get version info using Git if available
find_package(Git) find_package(Git)
IF (NOT DEFINED CPACK_PACKAGE_VERSION)
IF (GIT_FOUND) IF (GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long --always execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long --always --abbrev=7
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DESCRIBE_STRING OUTPUT_VARIABLE GIT_DESCRIBE_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE)
IF (DEFINED ENV{APPVEYOR_REPO_BRANCH}) string(REPLACE "/" "-" GIT_DESCRIBE_STRING ${GIT_DESCRIBE_STRING})
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()
ELSE() ELSE()
SET (GIT_DESCRIBE_STRING "unknown-version") SET (GIT_DESCRIBE_STRING "unknown-version")
ENDIF () ENDIF ()
if (CMAKE_SYSTEM_NAME MATCHES "Windows") set(CPACK_PACKAGE_VERSION "${GIT_DESCRIBE_STRING}-python$ENV{PYTHON_VERSION}")
set(CPACK_GENERATOR "7Z") ENDIF()
else()
set(CPACK_GENERATOR "TXZ")
endif()
set(CPACK_PACKAGE_VERSION ${GIT_DESCRIBE_STRING}) set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/package") set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/package")
include(CPack) include(CPack)
+12 -9
View File
@@ -1,21 +1,24 @@
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) **This repository is no longer maintained. A more complete and up to date Python wrapper can be found in the [libloot repository](https://github.com/loot/libloot/tree/aaacebefa053a6b7504bf61a73d2783a5436ca25/python).**
[![Travis Build Status](https://travis-ci.org/loot/loot-api-python.svg?branch=master)](https://travis-ci.org/loot/loot-api-python)
![CI](https://github.com/loot/libloot-python/workflows/CI/badge.svg?branch=master&event=push)
[![Documentation Status](https://readthedocs.org/projects/loot-api-python/badge/)](http://loot-api-python.readthedocs.io/) [![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). Not everything in the API is exposed: coverage can be extended on request (or by pull request). 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 ## Downloads
Snapshot builds are available on [Bintray](https://bintray.com/wrinklyninja/loot/loot-api-python). The snapshot build archives are named like so: Releases are hosted on [GitHub](https://github.com/loot/libloot-python/releases).
Snapshot builds are available as artifacts from [GitHub Actions runs](https://github.com/loot/libloot-python/actions), though they are only kept for 90 days and can only be downloaded when logged into a GitHub account. 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-python3.7-win32.zip` was built using the revision with shortened commit ID `94de368`.
## Documentation ## Documentation
@@ -23,6 +26,6 @@ The documentation can be found online at [Read The Docs](http://loot-api-python.
## Build Instructions ## 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. Only Windows support has been tested, though Linux builds should also be possible.
-64
View File
@@ -1,64 +0,0 @@
os: Visual Studio 2015
version: "{build}-{branch}"
configuration: Release
environment:
bintray_auth_token:
secure: fBQJmUDK/EgEUWjjbo6bWcitczeFTJZT4OZ3ZZ4FoUT6soBsTWPQJnr8YEVMFhGP
install:
- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.1.0/delete_old_bintray_versions.py', "$env:APPVEYOR_BUILD_FOLDER\delete_old_bintray_versions.py")
before_build:
- cd %APPVEYOR_BUILD_FOLDER%
- ps: mkdir build
- cd build
- cmake .. -G "Visual Studio 14 2015"
build:
verbosity: minimal
project: 'c:\projects\loot-api-python\build\loot_api_python.sln'
test_script:
- cd %APPVEYOR_BUILD_FOLDER%\build\Release
- python -m unittest test
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.
- ps: $env:GIT_DESCRIBE = ((git describe --tags --long --always) | Out-String) -replace "`n|`r", ""
artifacts:
- path: build\package\loot_api_python-$(GIT_DESCRIBE)_$(APPVEYOR_REPO_BRANCH)-win32.7z
name: loot_api_python
deploy:
- provider: BinTray
username: wrinklyninja
api_key:
secure: fBQJmUDK/EgEUWjjbo6bWcitczeFTJZT4OZ3ZZ4FoUT6soBsTWPQJnr8YEVMFhGP
subject: wrinklyninja
repo: loot
package: loot-api-python
version: $(GIT_DESCRIBE)_$(APPVEYOR_REPO_BRANCH)
publish: true
artifact: loot_api_python
- provider: GitHub
tag: $(APPVEYOR_REPO_TAG_NAME)
release: LOOT API Python Module v$(APPVEYOR_REPO_TAG_NAME)
description: |
Requires Windows 7 or later and the [MSVC 2015 x86 redistributable](https://download.microsoft.com/download/6/A/A/6AA4EDFF-645B-48C5-81CC-ED5963AEAD48/vc_redist.x86.exe), and [7-Zip](http://www.7-zip.org/) to extract the archive.
auth_token:
secure: V1GojGQfrAiQtUWtNNXfm3samiH+1yNF5UYhkn8B8mbif20IGTGWNlBS9E4S0SUC
artifact: loot_api_python
draft: false
on:
appveyor_repo_tag: true
on_success:
- ps: python "$env:APPVEYOR_BUILD_FOLDER\delete_old_bintray_versions.py" -o loot -g loot-api-python -u wrinklyninja -b loot -p loot-api-python -t $env:bintray_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 2022 x86 redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe)
to be installed. to be installed.
See the [online documentation](http://loot-api-python.readthedocs.org/) for more information. 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).
--- ---
+9 -9
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- 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. # sphinx-quickstart on Mon Aug 15 07:11:32 2016.
# #
# This file is execfile()d with the current directory set to its # This file is execfile()d with the current directory set to its
@@ -54,8 +54,8 @@ source_suffix = '.rst'
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
project = u'LOOT API Python Module' project = u'libloot-python'
copyright = u'2016, WrinklyNinja' copyright = u'2019, WrinklyNinja'
author = u'WrinklyNinja' author = u'WrinklyNinja'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
@@ -139,7 +139,7 @@ html_theme = 'sphinx_rtd_theme'
# The name for this set of Sphinx documents. # The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default. # "<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. # A shorter title for the navigation bar. Default is the same as html_title.
# #
@@ -265,7 +265,7 @@ latex_elements = {
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'LOOTAPIPythonModule.tex', u'LOOT API Python Module Documentation', (master_doc, 'libloot-python.tex', u'libloot-python Documentation',
u'WrinklyNinja', 'manual'), u'WrinklyNinja', 'manual'),
] ]
@@ -307,7 +307,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
(master_doc, 'lootapipythonmodule', u'LOOT API Python Module Documentation', (master_doc, 'libloot-python', u'libloot-python Documentation',
[author], 1) [author], 1)
] ]
@@ -322,8 +322,8 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'LOOTAPIPythonModule', u'LOOT API Python Module Documentation', (master_doc, 'libloot-python', u'libloot-python Documentation',
author, 'LOOTAPIPythonModule', 'One line description of project.', author, 'libloot-python', 'One line description of project.',
'Miscellaneous'), 'Miscellaneous'),
] ]
@@ -346,5 +346,5 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = { intersphinx_mapping = {
'loot_api': ('http://loot.readthedocs.io/en/0.10.2/', None), 'loot_api': ('http://loot.readthedocs.io/en/0.14.7/', 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. sphinx-quickstart on Mon Aug 15 07:11:32 2016.
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. contain the root `toctree` directive.
LOOT API Python Module libloot-python
====================== ==============
Contents: Contents:
+32 -38
View File
@@ -2,8 +2,8 @@
API Reference API Reference
************* *************
As this API is just a wrapper for LOOT's C++ API, its documentation is linked to As this API is just a wrapper for libloot's C++ API, its documentation is linked
for all non-Python-specific information. to for all non-Python-specific information.
Enumerations Enumerations
============ ============
@@ -12,9 +12,9 @@ The wrapped enumeration types below are classes in Python, but the distinction
makes no difference in practice, so they're grouped here for semantics. All makes no difference in practice, so they're grouped here for semantics. All
values are unsigned integer constants. values are unsigned integer constants.
.. py:class:: loot_api.GameType .. py:class:: loot.GameType
Wraps :cpp:type:`loot::GameType` to expose the LOOT API's game codes. Wraps :cpp:type:`loot::GameType` to expose libloot's game codes.
.. py:attribute:: fo3 .. py:attribute:: fo3
.. py:attribute:: fo4 .. py:attribute:: fo4
@@ -23,9 +23,9 @@ values are unsigned integer constants.
.. py:attribute:: tes5 .. py:attribute:: tes5
.. py:attribute:: tes5se .. py:attribute:: tes5se
.. py:class:: loot_api.LogLevel .. py:class:: loot.LogLevel
Wraps :cpp:type:`loot::LogLevel` to expose the LOOT API's log level codes. Wraps :cpp:type:`loot::LogLevel` to expose libloot's log level codes.
.. py:attribute:: trace .. py:attribute:: trace
.. py:attribute:: debug .. py:attribute:: debug
@@ -34,16 +34,16 @@ values are unsigned integer constants.
.. py:attribute:: error .. py:attribute:: error
.. py:attribute:: fatal .. py:attribute:: fatal
.. py:class:: loot_api.MessageType .. py:class:: loot.MessageType
Wraps :cpp:type:`loot::MessageType` to expose the LOOT API's message type Wraps :cpp:type:`loot::MessageType` to expose libloot's message type
codes. codes.
.. py:attribute:: error .. py:attribute:: error
.. py:attribute:: say .. py:attribute:: say
.. py:attribute:: warn .. py:attribute:: warn
.. py:class:: loot_api.PluginCleanliness .. py:class:: loot.PluginCleanliness
Codes used to indicate the cleanliness of a plugin according to the Codes used to indicate the cleanliness of a plugin according to the
information contained within the loaded masterlist/userlist. information contained within the loaded masterlist/userlist.
@@ -70,7 +70,7 @@ Public-Field Data Structures
Classes with public fields and no member functions. Classes with public fields and no member functions.
.. py:class:: loot_api.MasterlistInfo .. py:class:: loot.MasterlistInfo
Wraps :cpp:class:`loot::MasterlistInfo`. Wraps :cpp:class:`loot::MasterlistInfo`.
@@ -87,13 +87,13 @@ Classes with public fields and no member functions.
A boolean that is true if the masterlist has been modified from its state A boolean that is true if the masterlist has been modified from its state
at the commit given by :py:attr:`revision_id`. at the commit given by :py:attr:`revision_id`.
.. py:class:: loot_api.SimpleMessage .. py:class:: loot.SimpleMessage
Wraps :cpp:class:`loot::SimpleMessage`. Wraps :cpp:class:`loot::SimpleMessage`.
.. py:attribute:: type .. py:attribute:: type
A :py:class:`loot_api.MessageType` giving the message type. A :py:class:`loot.MessageType` giving the message type.
.. py:attribute:: language .. py:attribute:: language
@@ -107,7 +107,7 @@ Classes with public fields and no member functions.
A Unicode string containing the message condition. A Unicode string containing the message condition.
.. py:class:: loot_api.PluginTags .. py:class:: loot.PluginTags
Wraps :cpp:class:`loot::PluginTags`. Wraps :cpp:class:`loot::PluginTags`.
@@ -127,73 +127,68 @@ Classes with public fields and no member functions.
Functions Functions
========= =========
.. py:function:: loot_api.set_logging_callback(callback) -> NoneType .. py:function:: loot.set_logging_callback(callback) -> NoneType
Set the callback function that is called when logging. Wraps Set the callback function that is called when logging. Wraps
:cpp:func:`loot::SetLoggingCallback`. :cpp:func:`loot::SetLoggingCallback`.
.. py:function:: loot_api.is_compatible(int, int, int) -> bool .. py:function:: loot.is_compatible(int, int, int) -> bool
Checks for API compatibility. Wraps :cpp:func:`loot::IsCompatible`. Checks for API compatibility. Wraps :cpp:func:`loot::IsCompatible`.
.. py:function:: loot_api.InitialiseLocale(unicode) -> NoneType .. py:function:: loot.create_game_handle(game : loot.GameType, game_path : unicode, [game_local_path : unicode = u'']) -> loot.GameInterface
Initialise the current global locale using the given ID. Wraps
:cpp:func:`loot::InitialiseLocale`.
.. py:function:: loot_api.create_game_handle(game : loot_api.GameType, [game_path : unicode = u'', [game_local_path : unicode = u'']]) -> loot_api.GameInterface
Initialise a new game handle. Wraps :cpp:func:`loot::CreateGameHandle`. Initialise a new game handle. Wraps :cpp:func:`loot::CreateGameHandle`.
Classes Classes
======= =======
.. py:class:: loot_api.GameInterface .. py:class:: loot.GameInterface
Wraps :cpp:class:`loot::GameInterface`. Wraps :cpp:class:`loot::GameInterface`.
.. py:function:: loot_api.get_database() -> loot_api.DatabaseInterface .. py:function:: loot.get_database() -> loot.DatabaseInterface
Get a database handle. Wraps :cpp:func:`loot::GetDatabase`. Get a database handle. Wraps :cpp:func:`loot::GetDatabase`.
.. py:function:: loot_api.load_current_load_order_state() -> NoneType .. py:function:: loot.load_current_load_order_state() -> NoneType
Load the current load order state, discarding any previously held state. Load the current load order state, discarding any previously held state.
Wraps :cpp:func:`loot::LoadCurrentLoadOrderState`. Wraps :cpp:func:`loot::LoadCurrentLoadOrderState`.
.. py:class:: loot_api.DatabaseInterface .. py:class:: loot.DatabaseInterface
Wraps :cpp:class:`loot::DatabaseInterface`. Wraps :cpp:class:`loot::DatabaseInterface`.
.. py:method:: get_masterlist_revision(loot_api.DatabaseInterface, unicode, bool) -> loot_api.MasterlistInfo .. py:method:: get_masterlist_revision(loot.DatabaseInterface, unicode, bool) -> loot.MasterlistInfo
Gets the give masterlist’s source control revision. Wraps :cpp:func:`GetMasterlistRevision`. Gets the give masterlist’s source control revision. Wraps :cpp:func:`GetMasterlistRevision`.
.. py:method:: get_plugin_metadata(loot_api.DatabaseInterface, plugin : unicode, [includeUserMetadata : bool = True, [evaluateConditions : bool = False]]) -> loot_api.PluginMetadata .. py:method:: get_plugin_metadata(loot.DatabaseInterface, plugin : unicode, [includeUserMetadata : bool = True, [evaluateConditions : bool = False]]) -> loot.PluginMetadata
Get all a plugin’s loaded metadata. Wraps :cpp:func:`GetPluginMetadata`. Get all a plugin’s loaded metadata. Wraps :cpp:func:`GetPluginMetadata`.
.. py:method:: get_plugin_cleanliness(loot_api.DatabaseInterface, plugin : unicode, [evaluateConditions : bool = False]) -> loot_api.PluginCleanliness .. py:method:: get_plugin_cleanliness(loot.DatabaseInterface, plugin : unicode, [evaluateConditions : bool = False]) -> loot.PluginCleanliness
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. 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.
.. py:method:: get_plugin_tags(loot_api.DatabaseInterface, plugin : unicode, [evaluateConditions : bool = False]) -> loot_api.PluginTags .. py:method:: get_plugin_tags(loot.DatabaseInterface, plugin : unicode, [evaluateConditions : bool = False]) -> loot.PluginTags
Outputs the Bash Tags suggested for addition and removal by the database for the given plugin. Outputs the Bash Tags suggested for addition and removal by the database for the given plugin.
.. py:method:: load_lists(loot_api.DatabaseInterface, masterlist_path : unicode, [userlist_path : unicode = u'']) -> NoneType .. py:method:: load_lists(loot.DatabaseInterface, masterlist_path : unicode, [userlist_path : unicode = u'']) -> NoneType
Loads the masterlist and userlist from the paths specified. Wraps :cpp:func:`LoadLists`. Loads the masterlist and userlist from the paths specified. Wraps :cpp:func:`LoadLists`.
.. py:method:: update_masterlist(loot_api.DatabaseInterface, unicode, unicode, unicode) -> bool .. py:method:: update_masterlist(loot.DatabaseInterface, unicode, unicode, unicode) -> bool
Updates the given masterlist using the given Git repository details. Wraps :cpp:func:`UpdateMasterlist`. 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 .. py:method:: write_minimal_list(loot.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`. 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`. Wraps :cpp:class:`loot::LootVersion`.
@@ -217,10 +212,9 @@ Classes
Returns the API version as a string of the form ``major.minor.patch`` 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 Provides information about the version of libloot-python that is being run.
being run.
.. py:attribute:: major .. py:attribute:: major
@@ -242,11 +236,11 @@ Classes
Returns the API version as a string of the form ``major.minor.patch`` Returns the API version as a string of the form ``major.minor.patch``
.. py:class:: loot_api.PluginMetadata .. py:class:: loot.PluginMetadata
Wraps :cpp:class:`loot::PluginMetadata`. Wraps :cpp:class:`loot::PluginMetadata`.
.. py:method:: get_simple_messages(loot_api.PluginMetadata, unicode) -> list<loot_api.SimpleMessage> .. py:method:: get_simple_messages(loot.PluginMetadata, unicode) -> list<loot.SimpleMessage>
Get the plugin’s messages as SimpleMessage objects for the given language. Get the plugin’s messages as SimpleMessage objects for the given language.
Wraps :cpp:func:`GetPluginMessages`. Wraps :cpp:func:`GetPluginMessages`.
+10 -10
View File
@@ -7,16 +7,16 @@ Installing the wrapper
Build archives contain two binaries: Build archives contain two binaries:
* ``loot_api.pyd`` is the Python wrapper * ``loot.*.pyd`` is the Python wrapper
* ``loot_api.dll`` is the C++ API DLL that the Python wrapper was built against. * ``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++ 2022 Redistributable (x86)`_
to be installed. to be installed.
To use the wrapper, copy both files to wherever you want to import them from 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! (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++ 2022 Redistributable (x86): https://aka.ms/vs/17/release/vc_redist.x86.exe
Using the wrapper 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 To check if the module loaded is compatible with the version of the API that you
developed against:: developed against::
>>> import loot_api >>> import loot
>>> loot_api.is_compatible(0,10,0) >>> loot.is_compatible(0,14,0)
True True
>>> loot_api.is_compatible(0,9,0) >>> loot.is_compatible(0,9,0)
False False
Getting a Plugin's Bash Tag Suggestions 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:: To get a plugin's Bash Tag suggestions from a ``masterlist.yaml`` metadata file::
>>> import loot_api >>> import loot
>>> db = loot_api.create_database(loot_api.GameType.tes4, '', '') >>> db = loot.create_game_handle(loot.GameType.tes4, 'C:\\path\\to\\oblivion\\directory')
>>> db.load_lists('masterlist.yaml', '') >>> db.load_lists('masterlist.yaml')
>>> tags = db.get_plugin_tags(u'Unofficial Oblivion Patch.esp') >>> tags = db.get_plugin_tags(u'Unofficial Oblivion Patch.esp')
>>> tags.added >>> 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']) 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 *
+88
View File
@@ -0,0 +1,88 @@
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 :: 3',
'Programming Language :: C++'
'Operating System :: Microsoft :: Windows',
'Intended Audience :: Developers',
],
python_requires='>=3.7',
)
+21 -12
View File
@@ -31,21 +31,25 @@ PluginTags GetPluginTags(const std::shared_ptr<DatabaseInterface> db, const std:
PluginTags tags; PluginTags tags;
auto metadata = db->GetPluginMetadata(plugin, false, evaluateConditions); auto metadata = db->GetPluginMetadata(plugin, false, evaluateConditions);
for (const auto &tag : metadata.GetTags()) { if (metadata.has_value()) {
for (const auto &tag : metadata.value().GetTags()) {
if (tag.IsAddition()) if (tag.IsAddition())
tags.added.insert(tag.GetName()); tags.added.insert(tag.GetName());
else else
tags.removed.insert(tag.GetName()); tags.removed.insert(tag.GetName());
} }
}
metadata = db->GetPluginUserMetadata(plugin, evaluateConditions); metadata = db->GetPluginUserMetadata(plugin, evaluateConditions);
tags.userlist_modified = !metadata.GetTags().empty(); if (metadata.has_value()) {
for (const auto &tag : metadata.GetTags()) { tags.userlist_modified = !metadata.value().GetTags().empty();
for (const auto &tag : metadata.value().GetTags()) {
if (tag.IsAddition()) if (tag.IsAddition())
tags.added.insert(tag.GetName()); tags.added.insert(tag.GetName());
else else
tags.removed.insert(tag.GetName()); tags.removed.insert(tag.GetName());
} }
}
return tags; return tags;
} }
@@ -53,22 +57,27 @@ PluginTags GetPluginTags(const std::shared_ptr<DatabaseInterface> db, const std:
PluginCleanliness GetPluginCleanliness(const std::shared_ptr<DatabaseInterface> db, const std::string& plugin, bool evaluateConditions) { PluginCleanliness GetPluginCleanliness(const std::shared_ptr<DatabaseInterface> db, const std::string& plugin, bool evaluateConditions) {
auto metadata = db->GetPluginMetadata(plugin, true, evaluateConditions); auto metadata = db->GetPluginMetadata(plugin, true, evaluateConditions);
if (metadata.GetDirtyInfo().empty()) { if (!metadata.has_value()) {
if (metadata.GetCleanInfo().empty()) {
return PluginCleanliness::unknown;
} else {
return PluginCleanliness::clean;
}
} else if (!metadata.GetCleanInfo().empty()) {
return PluginCleanliness::unknown; return PluginCleanliness::unknown;
} }
for (const auto& info : metadata.GetDirtyInfo()) { 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) { if (info.ChooseInfo("en").GetText().find("Do not clean") != std::string::npos) {
return PluginCleanliness::do_not_clean; return PluginCleanliness::do_not_clean;
} }
} }
return PluginCleanliness::unknown; return PluginCleanliness::dirty;
} }
} }
+2 -2
View File
@@ -22,8 +22,8 @@ along with LOOT. If not, see
<https://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.
*/ */
#ifndef LOOT_API_PYTHON_CONVENIENCE #ifndef LIBLOOT_PYTHON_CONVENIENCE
#define LOOT_API_PYTHON_CONVENIENCE #define LIBLOOT_PYTHON_CONVENIENCE
#include <memory> #include <memory>
+43 -12
View File
@@ -34,16 +34,42 @@ using pybind11::arg;
using pybind11::enum_; using pybind11::enum_;
using pybind11::class_; using pybind11::class_;
using pybind11::metaclass; using pybind11::metaclass;
using std::filesystem::path;
using std::filesystem::u8path;
namespace loot { namespace loot {
namespace py {
std::shared_ptr<GameInterface> CreateGameHandle(GameType gameType, std::string gamePath, std::string gameLocalPath) {
return CreateGameHandle(gameType, u8path(gamePath), u8path(gameLocalPath));
}
void LoadLists(std::shared_ptr<DatabaseInterface> db, std::string masterlistPath, std::string userlistPath) {
return db->LoadLists(u8path(masterlistPath), u8path(userlistPath));
}
void WriteMinimalList(std::shared_ptr<DatabaseInterface> db, std::string outputFile, bool overwrite) {
return db->WriteMinimalList(u8path(outputFile), overwrite);
}
bool UpdateMasterlist(std::shared_ptr<DatabaseInterface> db, std::string masterlistPath, std::string remoteUrl, std::string remoteBranch) {
return db->UpdateMasterlist(u8path(masterlistPath), remoteUrl, remoteBranch);
}
MasterlistInfo GetMasterlistRevision(std::shared_ptr<DatabaseInterface> db, std::string masterlistPath, bool getShortId) {
return db->GetMasterlistRevision(u8path(masterlistPath), getShortId);
}
}
void bindEnums(pybind11::module& module) { void bindEnums(pybind11::module& module) {
enum_<GameType>(module, "GameType") enum_<GameType>(module, "GameType")
.value("tes4", GameType::tes4) .value("tes4", GameType::tes4)
.value("tes5", GameType::tes5) .value("tes5", GameType::tes5)
.value("tes5se", GameType::tes5se) .value("tes5se", GameType::tes5se)
.value("tes5vr", GameType::tes5vr)
.value("fo3", GameType::fo3) .value("fo3", GameType::fo3)
.value("fonv", GameType::fonv) .value("fonv", GameType::fonv)
.value("fo4", GameType::fo4); .value("fo4", GameType::fo4)
.value("fo4vr", GameType::fo4vr);
enum_<LogLevel>(module, "LogLevel") enum_<LogLevel>(module, "LogLevel")
.value("trace", LogLevel::trace) .value("trace", LogLevel::trace)
@@ -92,7 +118,7 @@ void bindVersionClasses(pybind11::module& module) {
.def_readonly_static("minor", &LootVersion::minor) .def_readonly_static("minor", &LootVersion::minor)
.def_readonly_static("patch", &LootVersion::patch) .def_readonly_static("patch", &LootVersion::patch)
.def_readonly_static("revision", &LootVersion::revision) .def_readonly_static("revision", &LootVersion::revision)
.def_static("string", LootVersion::string); .def_static("string", LootVersion::GetVersionString);
class_<WrapperVersion>(module, "WrapperVersion") class_<WrapperVersion>(module, "WrapperVersion")
.def_readonly_static("major", &WrapperVersion::major) .def_readonly_static("major", &WrapperVersion::major)
@@ -108,9 +134,9 @@ void bindInterfaceClasses(pybind11::module& module) {
.def("get_database", &GameInterface::GetDatabase); .def("get_database", &GameInterface::GetDatabase);
class_<DatabaseInterface, std::shared_ptr<DatabaseInterface>>(module, "DatabaseInterface") class_<DatabaseInterface, std::shared_ptr<DatabaseInterface>>(module, "DatabaseInterface")
.def("load_lists", &DatabaseInterface::LoadLists, arg("masterlist_path"), arg("userlist_path") = "") .def("load_lists", &py::LoadLists, arg("masterlist_path"), arg("userlist_path") = "")
.def("update_masterlist", &DatabaseInterface::UpdateMasterlist) .def("update_masterlist", &py::UpdateMasterlist)
.def("get_masterlist_revision", &DatabaseInterface::GetMasterlistRevision) .def("get_masterlist_revision", &py::GetMasterlistRevision)
.def("get_plugin_metadata", &DatabaseInterface::GetPluginMetadata, .def("get_plugin_metadata", &DatabaseInterface::GetPluginMetadata,
arg("plugin"), arg("plugin"),
arg("includeUserMetadata") = true, arg("includeUserMetadata") = true,
@@ -121,7 +147,7 @@ void bindInterfaceClasses(pybind11::module& module) {
.def("get_plugin_cleanliness", &GetPluginCleanliness, .def("get_plugin_cleanliness", &GetPluginCleanliness,
arg("plugin"), arg("plugin"),
arg("evaluateConditions") = false) arg("evaluateConditions") = false)
.def("write_minimal_list", &DatabaseInterface::WriteMinimalList); .def("write_minimal_list", &py::WriteMinimalList);
} }
void bindClasses(pybind11::module& module) { void bindClasses(pybind11::module& module) {
@@ -133,19 +159,24 @@ void bindClasses(pybind11::module& module) {
void bindFunctions(pybind11::module& module) { void bindFunctions(pybind11::module& module) {
module.def("set_logging_callback", &SetLoggingCallback); module.def("set_logging_callback", &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); module.def("is_compatible", &IsCompatible);
module.def("initialise_locale", &InitialiseLocale); module.def("create_game_handle", &py::CreateGameHandle,
module.def("create_game_handle", &CreateGameHandle,
arg("game"), arg("game"),
arg("game_path") = "", arg("game_path"),
arg("game_local_path") = ""); arg("game_local_path") = "");
} }
} }
PYBIND11_MODULE(loot_api, module) { PYBIND11_MODULE(loot, module) {
module.doc() = "A Python module that wraps the LOOT API, generated by pybind11."; module.doc() = "A Python module that wraps libloot, generated by pybind11.";
loot::bindEnums(module); loot::bindEnums(module);
loot::bindClasses(module); loot::bindClasses(module);
+3 -3
View File
@@ -21,11 +21,11 @@
along with LOOT. If not, see along with LOOT. If not, see
<https://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.
*/ */
#ifndef LOOT_PLUGIN_CLEANLINESS #ifndef LIBLOOT_PYTHON_PLUGIN_CLEANLINESS
#define LOOT_PLUGIN_CLEANLINESS #define LIBLOOT_PYTHON_PLUGIN_CLEANLINESS
/** /**
* The namespace used by the LOOT API. * The namespace used by libloot.
*/ */
namespace loot { namespace loot {
/** /**
+2 -2
View File
@@ -21,8 +21,8 @@
along with LOOT. If not, see along with LOOT. If not, see
<https://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.
*/ */
#ifndef LOOT_PLUGIN_TAGS #ifndef LIBLOOT_PYTHON_PLUGIN_TAGS
#define LOOT_PLUGIN_TAGS #define LIBLOOT_PYTHON_PLUGIN_TAGS
#include <set> #include <set>
#include <string> #include <string>
+3 -3
View File
@@ -25,9 +25,9 @@
#include "wrapper_version.h" #include "wrapper_version.h"
namespace loot { namespace loot {
const unsigned int WrapperVersion::major = 3; const unsigned int WrapperVersion::major = @LIBLOOT_PY_VERSION_MAJOR@;
const unsigned int WrapperVersion::minor = 0; const unsigned int WrapperVersion::minor = @LIBLOOT_PY_VERSION_MINOR@;
const unsigned int WrapperVersion::patch = 0; const unsigned int WrapperVersion::patch = @LIBLOOT_PY_VERSION_PATCH@;
const std::string WrapperVersion::revision = "@GIT_COMMIT_STRING@"; const std::string WrapperVersion::revision = "@GIT_COMMIT_STRING@";
std::string WrapperVersion::string() { std::string WrapperVersion::string() {
+2 -2
View File
@@ -22,8 +22,8 @@ along with LOOT. If not, see
<https://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.
*/ */
#ifndef LOOT_API_PYTHON_WRAPPER_VERSION #ifndef LIBLOOT_PYTHON_WRAPPER_VERSION
#define LOOT_API_PYTHON_WRAPPER_VERSION #define LIBLOOT_PYTHON_WRAPPER_VERSION
#include <string> #include <string>

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