From 0ce8f99494399ecccb3078dd27c20f672c2901ab Mon Sep 17 00:00:00 2001 From: David Sansome Date: Tue, 15 Feb 2011 19:18:53 +0000 Subject: [PATCH] Add a utility to generate python API docs using epydoc --- CMakeLists.txt | 1 + doc/python/CMakeLists.txt | 24 ++++++ doc/python/generate_python_docs.cpp | 57 +++++++++++++ doc/python/generate_python_docs.py | 17 ++++ doc/python/generate_python_docs.qrc | 5 ++ src/scripting/python/player.sip | 7 +- src/scripting/python/pythonengine.cpp | 114 ++++++++++++++------------ src/scripting/python/pythonengine.h | 2 + 8 files changed, 173 insertions(+), 54 deletions(-) create mode 100644 doc/python/CMakeLists.txt create mode 100644 doc/python/generate_python_docs.cpp create mode 100644 doc/python/generate_python_docs.py create mode 100644 doc/python/generate_python_docs.qrc diff --git a/CMakeLists.txt b/CMakeLists.txt index 91091e2b6..68c79dc33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,6 +308,7 @@ add_subdirectory(tests) add_subdirectory(dist) add_subdirectory(tools/ultimate_lyrics_parser) add_subdirectory(scripts) +add_subdirectory(doc/python) option(WITH_DEBIAN OFF) if(WITH_DEBIAN) diff --git a/doc/python/CMakeLists.txt b/doc/python/CMakeLists.txt new file mode 100644 index 000000000..869bc7d67 --- /dev/null +++ b/doc/python/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.6) + +include_directories(${LIBGPOD_INCLUDE_DIRS}) +include_directories(${PYTHON_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../src) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../src) + +set(SOURCES + generate_python_docs.cpp +) + +set(RESOURCES + generate_python_docs.qrc +) + +qt4_add_resources(QRC ${RESOURCES}) + +add_executable(generate_python_docs EXCLUDE_FROM_ALL ${SOURCES} ${QRC}) +target_link_libraries(generate_python_docs clementine_lib) + +add_custom_target(pythondocs + generate_python_docs + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/doc/python/generate_python_docs.cpp b/doc/python/generate_python_docs.cpp new file mode 100644 index 000000000..93855bd3d --- /dev/null +++ b/doc/python/generate_python_docs.cpp @@ -0,0 +1,57 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#include + +#include +#include + +#include "playlist/playlistitem.h" +#include "scripting/scriptmanager.h" +#include "scripting/python/pythonengine.h" + +int main(int argc, char** argv) { + QApplication a(argc, argv); + + // Register some meta types required by the exposed classes + qRegisterMetaType("PlaylistItemPtr"); + + // Create the python engine + ScriptManager manager; + LanguageEngine* language_engine = + manager.EngineForLanguage(ScriptInfo::Language_Python); + PythonEngine* python_engine = qobject_cast(language_engine); + + // Initialise python + if (!python_engine->EnsureInitialised()) { + qFatal("Failed to initialise Python engine"); + } + + // Load the python script + QFile script(":/doc/python/generate_python_docs.py"); + script.open(QIODevice::ReadOnly); + QByteArray script_data = script.readAll(); + + // Run it + PyEval_AcquireLock(); + if (PyRun_SimpleString(script_data.constData()) != 0) { + qFatal("Could not execute generate_python_docs.py"); + } + PyEval_ReleaseLock(); + + return 0; +} diff --git a/doc/python/generate_python_docs.py b/doc/python/generate_python_docs.py new file mode 100644 index 000000000..7d437aaaf --- /dev/null +++ b/doc/python/generate_python_docs.py @@ -0,0 +1,17 @@ +import clementine +import epydoc.cli +import sys + +sys.argv = [ + "epydoc", + "--html", + "-o", "output", + "--introspect-only", + "-v", + "--name", "clementine", + "--url", "http://www.clementine-player.org", + "clementine", +] + +print "Running '%s'" % ' '.join(sys.argv) +epydoc.cli.cli() diff --git a/doc/python/generate_python_docs.qrc b/doc/python/generate_python_docs.qrc new file mode 100644 index 000000000..fbadc33de --- /dev/null +++ b/doc/python/generate_python_docs.qrc @@ -0,0 +1,5 @@ + + + generate_python_docs.py + + diff --git a/src/scripting/python/player.sip b/src/scripting/python/player.sip index 6a842c264..70a588430 100644 --- a/src/scripting/python/player.sip +++ b/src/scripting/python/player.sip @@ -45,7 +45,12 @@ signals: void PlaylistFinished(); void VolumeChanged(int volume); void Error(const QString& message); - void TrackSkipped(PlaylistItemPtr old_track); + + // Disabled for now because it causes: + // TypeError: C++ type 'PlaylistItemPtr' is not supported as a native Qt signal type + // void TrackSkipped(PlaylistItemPtr old_track); + + void Seeked(qlonglong microseconds); void ForceShowOSD(Song); diff --git a/src/scripting/python/pythonengine.cpp b/src/scripting/python/pythonengine.cpp index ed456c2bf..2a1647d91 100644 --- a/src/scripting/python/pythonengine.cpp +++ b/src/scripting/python/pythonengine.cpp @@ -93,73 +93,81 @@ const sipAPIDef* PythonEngine::GetSIPApi() { #endif } -Script* PythonEngine::CreateScript(const ScriptInfo& info) { - // Initialise Python if it hasn't been done yet - if (!initialised_) { - AddLogLine("Initialising python...", false); +bool PythonEngine::EnsureInitialised() { + if (initialised_) + return true; + + AddLogLine("Initialising python...", false); #ifdef Q_OS_WIN32 - // On Windows we statically link against SIP and PyQt, so add those modules - // to Python's inittab here. - PyImport_AppendInittab(const_cast("sip"), initsip); - PyImport_AppendInittab(const_cast("PyQt4.Qt"), initQt); - PyImport_AppendInittab(const_cast("PyQt4.QtCore"), initQtCore); - PyImport_AppendInittab(const_cast("PyQt4.QtGui"), initQtGui); - PyImport_AppendInittab(const_cast("PyQt4.QtNetwork"), initQtNetwork); + // On Windows we statically link against SIP and PyQt, so add those modules + // to Python's inittab here. + PyImport_AppendInittab(const_cast("sip"), initsip); + PyImport_AppendInittab(const_cast("PyQt4.Qt"), initQt); + PyImport_AppendInittab(const_cast("PyQt4.QtCore"), initQtCore); + PyImport_AppendInittab(const_cast("PyQt4.QtGui"), initQtGui); + PyImport_AppendInittab(const_cast("PyQt4.QtNetwork"), initQtNetwork); #endif - // Add the Clementine builtin module - PyImport_AppendInittab(const_cast("clementine"), initclementine); + // Add the Clementine builtin module + PyImport_AppendInittab(const_cast("clementine"), initclementine); - // Initialise python - Py_SetProgramName(const_cast("clementine")); - PyEval_InitThreads(); - Py_InitializeEx(0); + // Initialise python + Py_SetProgramName(const_cast("clementine")); + PyEval_InitThreads(); + Py_InitializeEx(0); - // Get the clementine module so we can put stuff in it - clementine_module_ = PyImport_ImportModule("clementine"); - if (!clementine_module_) { - AddLogLine("Failed to import the clementine module", true); - if (PyErr_Occurred()) { - PyErr_Print(); - } - Py_Finalize(); - return NULL; + // Get the clementine module so we can put stuff in it + clementine_module_ = PyImport_ImportModule("clementine"); + if (!clementine_module_) { + AddLogLine("Failed to import the clementine module", true); + if (PyErr_Occurred()) { + PyErr_Print(); } - sip_api_ = GetSIPApi(); + Py_Finalize(); + return false; + } + sip_api_ = GetSIPApi(); - // Add objects to the module - if (manager()->data().valid_) { - AddObject(manager()->data().library_->backend(), sipType_LibraryBackend, "library"); - AddObject(manager()->data().library_view_, sipType_LibraryView, "library_view"); - AddObject(manager()->data().player_, sipType_PlayerInterface, "player"); - AddObject(manager()->data().playlists_, sipType_PlaylistManagerInterface, "playlists"); - AddObject(manager()->data().radio_model_, sipType_RadioModel, "radio_model"); - AddObject(manager()->data().settings_dialog_, sipType_SettingsDialog, "settings_dialog"); - AddObject(manager()->data().task_manager_, sipType_TaskManager, "task_manager"); - } + // Add objects to the module + if (manager()->data().valid_) { + AddObject(manager()->data().library_->backend(), sipType_LibraryBackend, "library"); + AddObject(manager()->data().library_view_, sipType_LibraryView, "library_view"); + AddObject(manager()->data().player_, sipType_PlayerInterface, "player"); + AddObject(manager()->data().playlists_, sipType_PlaylistManagerInterface, "playlists"); + AddObject(manager()->data().radio_model_, sipType_RadioModel, "radio_model"); + AddObject(manager()->data().settings_dialog_, sipType_SettingsDialog, "settings_dialog"); + AddObject(manager()->data().task_manager_, sipType_TaskManager, "task_manager"); + } - AddObject(manager()->ui(), sipType_UIInterface, "ui"); - AddObject(this, sipType_PythonEngine, "pythonengine"); + AddObject(manager()->ui(), sipType_UIInterface, "ui"); + AddObject(this, sipType_PythonEngine, "pythonengine"); - // Create a module for scripts - PyImport_AddModule(kModulePrefix); + // Create a module for scripts + PyImport_AddModule(kModulePrefix); - // Run the startup script - this redirects sys.stdout and sys.stderr to our - // log handler. - QFile python_startup(":pythonstartup.py"); - python_startup.open(QIODevice::ReadOnly); - QByteArray python_startup_script = python_startup.readAll(); + // Run the startup script - this redirects sys.stdout and sys.stderr to our + // log handler. + QFile python_startup(":pythonstartup.py"); + python_startup.open(QIODevice::ReadOnly); + QByteArray python_startup_script = python_startup.readAll(); - if (PyRun_SimpleString(python_startup_script.constData()) != 0) { - AddLogLine("Could not execute startup code", true); - Py_Finalize(); - return NULL; - } + if (PyRun_SimpleString(python_startup_script.constData()) != 0) { + AddLogLine("Could not execute startup code", true); + Py_Finalize(); + return false; + } - PyEval_ReleaseLock(); + PyEval_ReleaseLock(); - initialised_ = true; + initialised_ = true; + return true; +} + +Script* PythonEngine::CreateScript(const ScriptInfo& info) { + // Initialise Python if it hasn't been done yet + if (!EnsureInitialised()) { + return NULL; } Script* ret = new PythonScript(this, info); diff --git a/src/scripting/python/pythonengine.h b/src/scripting/python/pythonengine.h index 5b5edb505..a82984748 100644 --- a/src/scripting/python/pythonengine.h +++ b/src/scripting/python/pythonengine.h @@ -38,6 +38,8 @@ public: ScriptInfo::Language language() const { return ScriptInfo::Language_Python; } QString name() const { return "python"; } + bool EnsureInitialised(); + Script* CreateScript(const ScriptInfo& info); void DestroyScript(Script* script);