mirror of https://github.com/KLayout/klayout.git
First version of Python module.
This commit is contained in:
parent
108f90e382
commit
0b0393ce56
|
|
@ -39,6 +39,8 @@ equals(HAVE_PYTHON, "1") {
|
|||
SUBDIRS += pya
|
||||
LANG_DEPENDS += pya
|
||||
pya.depends += gsi db
|
||||
SUBDIRS += pymod
|
||||
pymod.depends += pya
|
||||
} else {
|
||||
SUBDIRS += pyastub
|
||||
pyastub.depends += gsi
|
||||
|
|
|
|||
|
|
@ -1190,10 +1190,7 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
|
|||
const gsi::MethodBase::MethodSynonym &syn = i->second.first->begin_synonyms () [i->second.second];
|
||||
|
||||
DocumentationParser method_doc (i->second.first);
|
||||
std::string pydoc;
|
||||
if (pya::PythonInterpreter::instance ()) {
|
||||
pydoc = pya::PythonInterpreter::instance ()->python_doc (i->second.first);
|
||||
}
|
||||
std::string pydoc = pya::PythonModule::python_doc (i->second.first);
|
||||
|
||||
os << "<tr>";
|
||||
if (i->first != prev_title) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -34,6 +34,7 @@
|
|||
#include <list>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
struct _typeobject;
|
||||
typedef _typeobject PyTypeObject;
|
||||
|
|
@ -89,6 +90,84 @@ public:
|
|||
|
||||
class MethodTable;
|
||||
|
||||
/**
|
||||
* @brief A representative for a Python module
|
||||
* Instantiate this object to represent a python module.
|
||||
* If used externally (for a library), call init with the module name.
|
||||
* Then call make_classes to create the individual classes.
|
||||
*/
|
||||
class PYA_PUBLIC PythonModule
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
PythonModule ();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~PythonModule ();
|
||||
|
||||
/**
|
||||
* @brief Initializes the module
|
||||
* This entry point is for external use where the module has not been created yet
|
||||
*/
|
||||
void init (const char *mod_name, const char *description);
|
||||
|
||||
/**
|
||||
* @brief Initializes the module
|
||||
* This entry point is for internal use where the module is created by the interpreter
|
||||
*/
|
||||
void init (const char *mod_name, PyObject *module);
|
||||
|
||||
/**
|
||||
* @brief Creates the classes after init has been called
|
||||
*/
|
||||
void make_classes ();
|
||||
|
||||
/**
|
||||
* @brief Gets the GSI class for a Python class
|
||||
*/
|
||||
static const gsi::ClassBase *cls_for_type (PyTypeObject *type);
|
||||
|
||||
/**
|
||||
* @brief The reverse: gets a Python class for a GSI class or NULL if there is no binding
|
||||
*/
|
||||
static PyTypeObject *type_for_cls (const gsi::ClassBase *cls);
|
||||
|
||||
/**
|
||||
* @brief Returns additional Python-specific documentation for the given method
|
||||
* If no specific documentation exists, an empty string is returned.
|
||||
*/
|
||||
static std::string python_doc (const gsi::MethodBase *method);
|
||||
|
||||
/**
|
||||
* @brief Gets the PyObject for the module
|
||||
*/
|
||||
PyObject *module ();
|
||||
|
||||
private:
|
||||
void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc);
|
||||
PyMethodDef *make_method_def ();
|
||||
PyGetSetDef *make_getset_def ();
|
||||
char *make_string (const std::string &s);
|
||||
|
||||
std::list<std::string> m_string_heap;
|
||||
std::vector<PyMethodDef *> m_methods_heap;
|
||||
std::vector<PyGetSetDef *> m_getseters_heap;
|
||||
|
||||
std::string m_mod_name, m_mod_description;
|
||||
std::string m_base_class_name;
|
||||
PythonRef mp_module;
|
||||
PythonRef mp_base_class;
|
||||
char *mp_mod_def;
|
||||
|
||||
static std::map<const gsi::MethodBase *, std::string> m_python_doc;
|
||||
static std::map <PyTypeObject *, const gsi::ClassBase *> m_cls_map;
|
||||
static std::map <const gsi::ClassBase *, PyTypeObject *> m_rev_cls_map;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The python interpreter wrapper class
|
||||
*/
|
||||
|
|
@ -214,16 +293,6 @@ public:
|
|||
*/
|
||||
std::string version () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the GSI class for a Python class
|
||||
*/
|
||||
const gsi::ClassBase *cls_for_type (PyTypeObject *type) const;
|
||||
|
||||
/**
|
||||
* @brief The reverse: gets a Python class for a GSI class or NULL if there is no binding
|
||||
*/
|
||||
PyTypeObject *type_for_cls (const gsi::ClassBase *cls) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the current console
|
||||
*/
|
||||
|
|
@ -246,12 +315,6 @@ public:
|
|||
*/
|
||||
int trace_func (PyFrameObject *frame, int event, PyObject *arg);
|
||||
|
||||
/**
|
||||
* @brief Returns additional Python-specific documentation for the given method
|
||||
* If no specific documentation exists, an empty string is returned.
|
||||
*/
|
||||
std::string python_doc (const gsi::MethodBase *method) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the singleton reference
|
||||
*/
|
||||
|
|
@ -266,22 +329,11 @@ private:
|
|||
size_t prepare_trace (PyObject *);
|
||||
tl::Variant eval_int (const char *string, const char *filename, int line, bool eval_expr, int context);
|
||||
void get_context (int context, PythonRef &globals, PythonRef &locals, const char *file);
|
||||
void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc);
|
||||
PyMethodDef *make_method_def ();
|
||||
PyGetSetDef *make_getset_def ();
|
||||
char *make_string (const std::string &s);
|
||||
|
||||
std::list<PythonRef> m_object_heap;
|
||||
std::list<std::string> m_string_heap;
|
||||
std::map<const gsi::MethodBase *, std::string> m_python_doc;
|
||||
std::set<std::string> m_package_paths;
|
||||
std::vector<PyMethodDef *> m_methods_heap;
|
||||
std::vector<PyGetSetDef *> m_getseters_heap;
|
||||
PythonRef m_stdout_channel, m_stderr_channel;
|
||||
PythonPtr m_stdout, m_stderr;
|
||||
std::set<std::string> m_package_paths;
|
||||
|
||||
std::map <PyTypeObject *, const gsi::ClassBase *> m_cls_map;
|
||||
std::map <const gsi::ClassBase *, PyTypeObject *> m_rev_cls_map;
|
||||
gsi::Console *mp_current_console;
|
||||
std::vector<gsi::Console *> m_consoles;
|
||||
gsi::ExecutionHandler *mp_current_exec_handler;
|
||||
|
|
@ -296,6 +348,7 @@ private:
|
|||
PyFrameObject *mp_current_frame;
|
||||
std::map<PyObject *, size_t> m_file_id_map;
|
||||
wchar_t *mp_py3_app_name;
|
||||
pya::PythonModule m_pya_module;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,32 +20,19 @@
|
|||
|
||||
*/
|
||||
|
||||
#include "tlDefs.h"
|
||||
|
||||
#if !defined(HDR_pyaCommon_h)
|
||||
# define HDR_pyaCommon_h
|
||||
|
||||
# if defined _WIN32 || defined __CYGWIN__
|
||||
|
||||
# ifdef MAKE_PYA_LIBRARY
|
||||
# define PYA_PUBLIC __declspec(dllexport)
|
||||
# else
|
||||
# define PYA_PUBLIC __declspec(dllimport)
|
||||
# endif
|
||||
# define PYA_LOCAL
|
||||
# define PYA_PUBLIC_TEMPLATE
|
||||
|
||||
# ifdef MAKE_PYA_LIBRARY
|
||||
# define PYA_PUBLIC DEF_INSIDE_PUBLIC
|
||||
# define PYA_PUBLIC_TEMPLATE DEF_INSIDE_PUBLIC_TEMPLATE
|
||||
# define PYA_LOCAL DEF_INSIDE_LOCAL
|
||||
# else
|
||||
|
||||
# if __GNUC__ >= 4 || defined(__clang__)
|
||||
# define PYA_PUBLIC __attribute__ ((visibility ("default")))
|
||||
# define PYA_PUBLIC_TEMPLATE __attribute__ ((visibility ("default")))
|
||||
# define PYA_LOCAL __attribute__ ((visibility ("hidden")))
|
||||
# else
|
||||
# define PYA_PUBLIC
|
||||
# define PYA_PUBLIC_TEMPLATE
|
||||
# define PYA_LOCAL
|
||||
# endif
|
||||
|
||||
# define PYA_PUBLIC DEF_OUTSIDE_PUBLIC
|
||||
# define PYA_PUBLIC_TEMPLATE DEF_OUTSIDE_PUBLIC_TEMPLATE
|
||||
# define PYA_LOCAL DEF_OUTSIDE_LOCAL
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ tl::Variant python2c<tl::Variant> (PyObject *rval, tl::Heap *heap)
|
|||
|
||||
} else {
|
||||
|
||||
const gsi::ClassBase *cls = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (rval));
|
||||
const gsi::ClassBase *cls = PythonModule::cls_for_type (Py_TYPE (rval));
|
||||
if (cls) {
|
||||
|
||||
PYAObjectBase *p = (PYAObjectBase *) rval;
|
||||
|
|
@ -438,7 +438,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo
|
|||
// can't guarantee the lifetime of the container will exceed that
|
||||
// of the exposed property. Hence copying is safer.
|
||||
|
||||
PyTypeObject *type = PythonInterpreter::instance ()->type_for_cls (clsact);
|
||||
PyTypeObject *type = PythonModule::type_for_cls (clsact);
|
||||
tl_assert (type != NULL);
|
||||
|
||||
// create a instance and copy the value
|
||||
|
|
@ -459,7 +459,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo
|
|||
|
||||
} else {
|
||||
|
||||
PyTypeObject *type = PythonInterpreter::instance ()->type_for_cls (clsact);
|
||||
PyTypeObject *type = PythonModule::type_for_cls (clsact);
|
||||
tl_assert (type != NULL);
|
||||
|
||||
// create a instance and copy the value
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ namespace pya
|
|||
// Serialization adaptors for strings, variants, vectors and maps
|
||||
|
||||
/**
|
||||
* @brief An adaptor for a string from ruby objects
|
||||
* @brief An adaptor for a string from ruby objects
|
||||
*/
|
||||
class PythonBasedStringAdaptor
|
||||
class PythonBasedStringAdaptor
|
||||
: public gsi::StringAdaptor
|
||||
{
|
||||
public:
|
||||
|
|
@ -69,9 +69,9 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief An adaptor for a variant from ruby objects
|
||||
* @brief An adaptor for a variant from ruby objects
|
||||
*/
|
||||
class PythonBasedVariantAdaptor
|
||||
class PythonBasedVariantAdaptor
|
||||
: public gsi::VariantAdaptor
|
||||
{
|
||||
public:
|
||||
|
|
@ -106,7 +106,7 @@ private:
|
|||
/**
|
||||
* @brief An adaptor for a vector from Python objects
|
||||
*/
|
||||
class PythonBasedVectorAdaptor
|
||||
class PythonBasedVectorAdaptor
|
||||
: public gsi::VectorAdaptor
|
||||
{
|
||||
public:
|
||||
|
|
@ -132,7 +132,7 @@ class PythonBasedMapAdaptorIterator
|
|||
public:
|
||||
PythonBasedMapAdaptorIterator (const PythonPtr &hash, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
|
||||
|
||||
virtual void get (gsi::SerialArgs &w, tl::Heap &heap) const;
|
||||
virtual void get (gsi::SerialArgs &w, tl::Heap &heap) const;
|
||||
virtual bool at_end () const;
|
||||
virtual void inc ();
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ private:
|
|||
/**
|
||||
* @brief An adaptor for a map from Python objects
|
||||
*/
|
||||
class PythonBasedMapAdaptor
|
||||
class PythonBasedMapAdaptor
|
||||
: public gsi::MapAdaptor
|
||||
{
|
||||
public:
|
||||
|
|
@ -171,8 +171,8 @@ template <class R>
|
|||
struct get_boxed_value_func
|
||||
{
|
||||
void operator() (void **ret, PyObject *arg, tl::Heap *heap)
|
||||
{
|
||||
const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
|
||||
{
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
|
||||
R *v = new R (python2c<R> (arg, heap));
|
||||
|
|
@ -187,7 +187,7 @@ struct get_boxed_value_func
|
|||
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Passing an object to pointer or reference requires a boxed type (pya.%s)")), bt->name ()));
|
||||
}
|
||||
|
||||
PYAObjectBase *p = (PYAObjectBase *) arg;
|
||||
PYAObjectBase *p = (PYAObjectBase *) arg;
|
||||
gsi::Value *bo = reinterpret_cast<gsi::Value *> (p->obj ());
|
||||
if (bo) {
|
||||
*ret = bo->value ().template morph<R> ().native_ptr ();
|
||||
|
|
@ -254,8 +254,8 @@ struct writer
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Serialization for strings
|
||||
/**
|
||||
* @brief Serialization for strings
|
||||
*/
|
||||
template <>
|
||||
struct writer<gsi::StringType>
|
||||
|
|
@ -362,7 +362,7 @@ struct writer<gsi::MapType>
|
|||
|
||||
/**
|
||||
* @brief A serialization wrapper (write mode)
|
||||
* Specialisation for objects
|
||||
* Specialisation for objects
|
||||
*/
|
||||
template <>
|
||||
struct writer<gsi::ObjectType>
|
||||
|
|
@ -382,14 +382,14 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
|
||||
|
||||
const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
}
|
||||
|
||||
if (cls_decl->is_derived_from (atype.cls ())) {
|
||||
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
|
||||
if (cls_decl->adapted_type_info ()) {
|
||||
// resolved adapted type
|
||||
|
|
@ -400,7 +400,7 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
} else if (cls_decl->can_convert_to (atype.cls ())) {
|
||||
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
|
||||
// We can convert objects for cref and cptr, but ownership over these objects is not transferred.
|
||||
// Hence we have to keep them on the heap.
|
||||
|
|
@ -414,14 +414,14 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
} else {
|
||||
|
||||
const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
}
|
||||
|
||||
if (cls_decl->is_derived_from (atype.cls ())) {
|
||||
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
|
||||
if (cls_decl->adapted_type_info ()) {
|
||||
// resolved adapted type
|
||||
|
|
@ -432,7 +432,7 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
} else if (cls_decl->can_convert_to (atype.cls ())) {
|
||||
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
PYAObjectBase *p = (PYAObjectBase *) (arg);
|
||||
aa->write<void *> (atype.cls ()->create_obj_from (cls_decl, p->obj ()));
|
||||
|
||||
} else {
|
||||
|
|
@ -446,7 +446,7 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
/**
|
||||
* @brief A serialization wrapper (write mode)
|
||||
* Specialisation for void
|
||||
* Specialisation for void
|
||||
*/
|
||||
template <>
|
||||
struct writer<gsi::VoidType>
|
||||
|
|
@ -464,11 +464,11 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *arg, tl
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Deseralisation wrapper
|
||||
* @brief Deseralisation wrapper
|
||||
*
|
||||
* The default implementation is for POD types, strings and variants
|
||||
*/
|
||||
template <class R>
|
||||
template <class R>
|
||||
struct reader
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
|
||||
|
|
@ -503,7 +503,7 @@ struct reader
|
|||
* Without that would would have to handle void *&, void * const &, ...
|
||||
* TODO: right now these types are not supported.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<void *>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
|
||||
|
|
@ -519,7 +519,7 @@ struct reader<void *>
|
|||
/**
|
||||
* @brief Deseralisation wrapper: specialization for strings
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<gsi::StringType>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &, tl::Heap *heap)
|
||||
|
|
@ -589,7 +589,7 @@ PyObject *object_from_variant (const tl::Variant &var, PYAObjectBase *self, cons
|
|||
/**
|
||||
* @brief Deseralisation wrapper: specialization for variants
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<gsi::VariantType>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase *self, const gsi::ArgType &atype, tl::Heap *heap)
|
||||
|
|
@ -612,7 +612,7 @@ struct reader<gsi::VariantType>
|
|||
/**
|
||||
* @brief Deseralisation wrapper: specialization for vectors
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<gsi::VectorType>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &atype, tl::Heap *heap)
|
||||
|
|
@ -624,7 +624,7 @@ struct reader<gsi::VectorType>
|
|||
*ret = PyList_New (0);
|
||||
tl_assert (atype.inner () != 0);
|
||||
PythonBasedVectorAdaptor t (*ret, atype.inner ());
|
||||
a->copy_to (&t, *heap);
|
||||
a->copy_to (&t, *heap);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -632,7 +632,7 @@ struct reader<gsi::VectorType>
|
|||
/**
|
||||
* @brief Deseralisation wrapper: specialization for maps
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<gsi::MapType>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &atype, tl::Heap *heap)
|
||||
|
|
@ -651,9 +651,9 @@ struct reader<gsi::MapType>
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Deseralisation wrapper: specialization for object
|
||||
* @brief Deseralisation wrapper: specialization for object
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<gsi::ObjectType>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase *self, const gsi::ArgType &atype, tl::Heap *heap)
|
||||
|
|
@ -670,7 +670,7 @@ struct reader<gsi::ObjectType>
|
|||
/**
|
||||
* @brief Deseralisation wrapper: specialization for void
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
struct reader<gsi::VoidType>
|
||||
{
|
||||
void operator() (gsi::SerialArgs *, PythonRef *, PYAObjectBase *, const gsi::ArgType &, tl::Heap *)
|
||||
|
|
@ -679,7 +679,7 @@ struct reader<gsi::VoidType>
|
|||
}
|
||||
};
|
||||
|
||||
PythonRef
|
||||
PythonRef
|
||||
pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap)
|
||||
{
|
||||
PythonRef ret;
|
||||
|
|
@ -701,7 +701,7 @@ tl::Variant PythonBasedVariantAdaptor::var () const
|
|||
return python2c<tl::Variant> (m_var.get ());
|
||||
}
|
||||
|
||||
void PythonBasedVariantAdaptor::set (const tl::Variant & /*v*/)
|
||||
void PythonBasedVariantAdaptor::set (const tl::Variant & /*v*/)
|
||||
{
|
||||
// TODO: is there a setter for a string?
|
||||
}
|
||||
|
|
@ -715,7 +715,7 @@ PythonBasedVectorAdaptorIterator::PythonBasedVectorAdaptorIterator (const Python
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void PythonBasedVectorAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
|
||||
void PythonBasedVectorAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
|
||||
{
|
||||
PyObject *member = NULL;
|
||||
if (PyTuple_Check (m_array.get ())) {
|
||||
|
|
@ -726,12 +726,12 @@ void PythonBasedVectorAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap)
|
|||
gsi::do_on_type<writer> () (mp_ainner->type (), &w, member, *mp_ainner, &heap);
|
||||
}
|
||||
|
||||
bool PythonBasedVectorAdaptorIterator::at_end () const
|
||||
bool PythonBasedVectorAdaptorIterator::at_end () const
|
||||
{
|
||||
return m_i == m_len;
|
||||
}
|
||||
|
||||
void PythonBasedVectorAdaptorIterator::inc ()
|
||||
void PythonBasedVectorAdaptorIterator::inc ()
|
||||
{
|
||||
++m_i;
|
||||
}
|
||||
|
|
@ -750,7 +750,7 @@ gsi::VectorAdaptorIterator *PythonBasedVectorAdaptor::create_iterator () const
|
|||
return new PythonBasedVectorAdaptorIterator (m_array, size (), mp_ainner);
|
||||
}
|
||||
|
||||
void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap)
|
||||
void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap)
|
||||
{
|
||||
if (PyList_Check (m_array.get ())) {
|
||||
PythonRef member;
|
||||
|
|
@ -761,7 +761,7 @@ void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap)
|
|||
}
|
||||
}
|
||||
|
||||
void PythonBasedVectorAdaptor::clear ()
|
||||
void PythonBasedVectorAdaptor::clear ()
|
||||
{
|
||||
if (PySequence_Check (m_array.get ())) {
|
||||
PySequence_DelSlice (m_array.get (), 0, PySequence_Length (m_array.get ()));
|
||||
|
|
@ -777,7 +777,7 @@ size_t PythonBasedVectorAdaptor::size () const
|
|||
}
|
||||
}
|
||||
|
||||
size_t PythonBasedVectorAdaptor::serial_size () const
|
||||
size_t PythonBasedVectorAdaptor::serial_size () const
|
||||
{
|
||||
return mp_ainner->size ();
|
||||
}
|
||||
|
|
@ -793,18 +793,18 @@ PythonBasedMapAdaptorIterator::PythonBasedMapAdaptorIterator (const PythonPtr &h
|
|||
inc ();
|
||||
}
|
||||
|
||||
void PythonBasedMapAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
|
||||
void PythonBasedMapAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
|
||||
{
|
||||
gsi::do_on_type<writer> () (mp_ainner_k->type (), &w, m_key, *mp_ainner_k, &heap);
|
||||
gsi::do_on_type<writer> () (mp_ainner->type (), &w, m_value, *mp_ainner, &heap);
|
||||
}
|
||||
|
||||
bool PythonBasedMapAdaptorIterator::at_end () const
|
||||
bool PythonBasedMapAdaptorIterator::at_end () const
|
||||
{
|
||||
return ! m_has_items;
|
||||
}
|
||||
|
||||
void PythonBasedMapAdaptorIterator::inc ()
|
||||
void PythonBasedMapAdaptorIterator::inc ()
|
||||
{
|
||||
m_has_items = PyDict_Next(m_hash.get (), &m_pos, &m_key, &m_value);
|
||||
}
|
||||
|
|
@ -813,7 +813,7 @@ void PythonBasedMapAdaptorIterator::inc ()
|
|||
// PythonBasedMapAdaptor implementation
|
||||
|
||||
PythonBasedMapAdaptor::PythonBasedMapAdaptor (const PythonPtr &hash, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
|
||||
: mp_ainner (ainner), mp_ainner_k (ainner_k), m_hash (hash)
|
||||
: mp_ainner (ainner), mp_ainner_k (ainner_k), m_hash (hash)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -822,7 +822,7 @@ gsi::MapAdaptorIterator *PythonBasedMapAdaptor::create_iterator () const
|
|||
return new PythonBasedMapAdaptorIterator (m_hash, mp_ainner, mp_ainner_k);
|
||||
}
|
||||
|
||||
void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap)
|
||||
void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap)
|
||||
{
|
||||
PythonRef k, v;
|
||||
gsi::do_on_type<reader> () (mp_ainner_k->type (), &r, &k, (PYAObjectBase *) 0, *mp_ainner_k, &heap);
|
||||
|
|
@ -830,7 +830,7 @@ void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap)
|
|||
PyDict_SetItem (m_hash.get (), k.get (), v.get ());
|
||||
}
|
||||
|
||||
void PythonBasedMapAdaptor::clear ()
|
||||
void PythonBasedMapAdaptor::clear ()
|
||||
{
|
||||
PyDict_Clear (m_hash.get ());
|
||||
}
|
||||
|
|
@ -840,7 +840,7 @@ size_t PythonBasedMapAdaptor::size () const
|
|||
return PyDict_Size (m_hash.get ());
|
||||
}
|
||||
|
||||
size_t PythonBasedMapAdaptor::serial_size () const
|
||||
size_t PythonBasedMapAdaptor::serial_size () const
|
||||
{
|
||||
return mp_ainner_k->size () + mp_ainner->size ();
|
||||
}
|
||||
|
|
@ -876,7 +876,7 @@ struct test_arg_func
|
|||
if (atype.is_ptr () || atype.is_ref ()) {
|
||||
|
||||
// check if we have a boxed type
|
||||
const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (cls_decl) {
|
||||
const gsi::ClassBase *bc = gsi::cls_decl <gsi::Value> ();
|
||||
if (cls_decl->is_derived_from (bc)) {
|
||||
|
|
@ -914,11 +914,11 @@ struct test_arg_func<gsi::StringType>
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyString_Check (arg)) {
|
||||
*ret = true;
|
||||
} else
|
||||
} else
|
||||
#else
|
||||
if (PyBytes_Check (arg)) {
|
||||
*ret = true;
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyUnicode_Check (arg)) {
|
||||
*ret = true;
|
||||
|
|
@ -981,7 +981,7 @@ struct test_arg_func<gsi::MapType>
|
|||
const gsi::ArgType &ainner = *atype.inner ();
|
||||
const gsi::ArgType &ainner_k = *atype.inner ();
|
||||
|
||||
// Note: we test key and value separately. That way we don't need to
|
||||
// Note: we test key and value separately. That way we don't need to
|
||||
// instantiate a 2d template with do_on_type2.
|
||||
*ret = true;
|
||||
|
||||
|
|
@ -1010,7 +1010,7 @@ struct test_arg_func<gsi::ObjectType>
|
|||
return;
|
||||
}
|
||||
|
||||
const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
*ret = false;
|
||||
return;
|
||||
|
|
@ -1037,6 +1037,5 @@ test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose)
|
|||
gsi::do_on_type<test_arg_func> () (atype.type (), &ret, arg, atype, loose);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ HEADERS = \
|
|||
tlUnitTest.h \
|
||||
tlInt128Support.h \
|
||||
tlHttpStreamCurl.h \
|
||||
tlHttpStreamQt.h
|
||||
tlHttpStreamQt.h \
|
||||
tlDefs.h
|
||||
|
||||
INCLUDEPATH =
|
||||
DEPENDPATH =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2018 Matthias Koefferlein
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#if !defined(HDR_tlDefs_h)
|
||||
# define HDR_tlDefs_h
|
||||
|
||||
// templates provided for building the external symbol
|
||||
// declarations per library
|
||||
|
||||
# if defined _WIN32 || defined __CYGWIN__
|
||||
|
||||
# define DEF_INSIDE_PUBLIC __declspec(dllexport)
|
||||
# define DEF_INSIDE_LOCAL
|
||||
# define DEF_INSIDE_PUBLIC_TEMPLATE
|
||||
|
||||
# define DEF_OUTSIDE_PUBLIC __declspec(dllimport)
|
||||
# define DEF_OUTSIDE_LOCAL
|
||||
# define DEF_OUTSIDE_PUBLIC_TEMPLATE
|
||||
|
||||
# else
|
||||
|
||||
# if __GNUC__ >= 4 || defined(__clang__)
|
||||
# define DEF_INSIDE_PUBLIC __attribute__ ((visibility ("default")))
|
||||
# define DEF_INSIDE_PUBLIC_TEMPLATE __attribute__ ((visibility ("default")))
|
||||
# define DEF_INSIDE_LOCAL __attribute__ ((visibility ("hidden")))
|
||||
# else
|
||||
# define DEF_INSIDE_PUBLIC
|
||||
# define DEF_INSIDE_PUBLIC_TEMPLATE
|
||||
# define DEF_INSIDE_LOCAL
|
||||
# endif
|
||||
|
||||
# define DEF_OUTSIDE_PUBLIC DEF_INSIDE_PUBLIC
|
||||
# define DEF_OUTSIDE_PUBLIC_TEMPLATE DEF_INSIDE_PUBLIC_TEMPLATE
|
||||
# define DEF_OUTSIDE_LOCAL DEF_INSIDE_LOCAL
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
@ -25,6 +25,7 @@ import gc
|
|||
# Set this to True to disable some tests involving exceptions
|
||||
leak_check = "TEST_LEAK_CHECK" in os.environ
|
||||
|
||||
print("@@@1")
|
||||
# see test_21
|
||||
class AEXT(pya.A):
|
||||
def __init__(self):
|
||||
|
|
@ -134,16 +135,23 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_00(self):
|
||||
|
||||
print("@@@00")
|
||||
# all references of PA are released now:
|
||||
ac0 = pya.A.a0()
|
||||
|
||||
print("@@@00-1")
|
||||
a = pya.A.new_a(100)
|
||||
self.assertEqual( pya.A.a0(), ac0 + 1 )
|
||||
print("@@@00-2")
|
||||
|
||||
a = pya.A()
|
||||
print("@@@00-31")
|
||||
self.assertEqual(a.a1(), 17)
|
||||
print("@@@00-32")
|
||||
a.assign(pya.A(110))
|
||||
print("@@@00-33")
|
||||
self.assertEqual(a.a1(), 110)
|
||||
print("@@@00-3")
|
||||
|
||||
a = None
|
||||
self.assertEqual( pya.A.a0(), ac0 )
|
||||
|
|
@ -384,6 +392,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_12(self):
|
||||
|
||||
print("@@@12")
|
||||
a1 = pya.A()
|
||||
a1.a5( -15 )
|
||||
a2 = a1
|
||||
|
|
@ -584,6 +593,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_13(self):
|
||||
|
||||
print("@@@13")
|
||||
b = pya.B()
|
||||
|
||||
if not leak_check:
|
||||
|
|
@ -810,6 +820,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_13b(self):
|
||||
|
||||
print("@@@13b")
|
||||
b = pya.B()
|
||||
|
||||
bb = pya.B()
|
||||
|
|
@ -913,6 +924,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_14(self):
|
||||
|
||||
print("@@@14")
|
||||
a = pya.A()
|
||||
a.a5( 22 )
|
||||
|
||||
|
|
@ -926,6 +938,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_15(self):
|
||||
|
||||
print("@@@15")
|
||||
a = pya.A_NC()
|
||||
self.assertEqual( True, isinstance(a, pya.A) )
|
||||
a.a5( 22 )
|
||||
|
|
@ -940,6 +953,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_16(self):
|
||||
|
||||
print("@@@16")
|
||||
if leak_check:
|
||||
return
|
||||
|
||||
|
|
@ -966,6 +980,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_17(self):
|
||||
|
||||
print("@@@17")
|
||||
# test copies of objects being returned
|
||||
|
||||
b = pya.B()
|
||||
|
|
@ -991,6 +1006,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_18(self):
|
||||
|
||||
print("@@@18")
|
||||
# Test references to objects (returned by b.b7)
|
||||
|
||||
b = pya.B()
|
||||
|
|
@ -1017,6 +1033,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_19(self):
|
||||
|
||||
print("@@@19")
|
||||
c0 = pya.C()
|
||||
self.assertEqual( c0.g("x"), 1977 );
|
||||
|
||||
|
|
@ -1062,6 +1079,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_20(self):
|
||||
|
||||
print("@@@20")
|
||||
b = pya.B()
|
||||
|
||||
a1 = b.b14a( True )
|
||||
|
|
@ -1081,6 +1099,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_21(self):
|
||||
|
||||
print("@@@21")
|
||||
# Python does not allow extending built-in types - the following test
|
||||
# is taken from the Ruby binding. I don't know how to implement it for
|
||||
# Python however.
|
||||
|
|
@ -1112,6 +1131,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_22(self):
|
||||
|
||||
print("@@@22")
|
||||
# test client data binding to C++ objects
|
||||
|
||||
b = pya.B()
|
||||
|
|
@ -1230,6 +1250,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_23(self):
|
||||
|
||||
print("@@@23")
|
||||
b = pya.B()
|
||||
a = pya.A()
|
||||
|
||||
|
|
@ -1277,6 +1298,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_24(self):
|
||||
|
||||
print("@@@24")
|
||||
n = [ 0, 0 , "" ]
|
||||
|
||||
# Events
|
||||
|
|
@ -1362,6 +1384,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_25(self):
|
||||
|
||||
print("@@@25")
|
||||
# destruction of an instance via c++
|
||||
pya.A.a20(None)
|
||||
ac0 = pya.A.a0()
|
||||
|
|
@ -1414,6 +1437,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_26(self):
|
||||
|
||||
print("@@@26")
|
||||
# cyclic references - event bound to itself
|
||||
|
||||
base_count = EEXT.inst_count()
|
||||
|
|
@ -1457,6 +1481,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_27(self):
|
||||
|
||||
print("@@@27")
|
||||
# destruction of an instance via c++
|
||||
pya.A.a20(None)
|
||||
ac0 = pya.A.a0()
|
||||
|
|
@ -1493,6 +1518,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_28(self):
|
||||
|
||||
print("@@@28")
|
||||
self.assertEqual(pya.B.inst() == None, True)
|
||||
self.assertEqual(pya.B.has_inst(), False)
|
||||
|
||||
|
|
@ -1528,6 +1554,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_30(self):
|
||||
|
||||
print("@@@30")
|
||||
# some basic tests for the *Value boxing classes
|
||||
|
||||
val = pya.Value()
|
||||
|
|
@ -1704,6 +1731,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_31(self):
|
||||
|
||||
print("@@@31")
|
||||
# some basic tests with derived and base classes
|
||||
|
||||
pya.X.init()
|
||||
|
|
@ -1760,6 +1788,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_32(self):
|
||||
|
||||
print("@@@32")
|
||||
# run test only if we have Qt bindings
|
||||
if not "QStringPair" in pya.__dict__:
|
||||
return
|
||||
|
|
@ -1788,6 +1817,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_33(self):
|
||||
|
||||
print("@@@33")
|
||||
def str_from_bytearray(ba):
|
||||
if (sys.version_info > (3, 0)):
|
||||
return ba
|
||||
|
|
@ -1822,6 +1852,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_34(self):
|
||||
|
||||
print("@@@34")
|
||||
# run test only if we have Qt bindings
|
||||
if not "QDialog" in pya.__dict__:
|
||||
return
|
||||
|
|
@ -1840,6 +1871,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_35(self):
|
||||
|
||||
print("@@@35")
|
||||
# vectors of pointers
|
||||
|
||||
pya.X.init()
|
||||
|
|
@ -1940,12 +1972,14 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_36(self):
|
||||
|
||||
print("@@@36")
|
||||
x = XEdge()
|
||||
self.assertEqual("XEdge", type(x).__name__)
|
||||
self.assertEqual("(1,2;3,4)", str(x))
|
||||
|
||||
def test_37(self):
|
||||
|
||||
print("@@@37")
|
||||
# This test is taken from the Ruby binding, but
|
||||
# Python does not have protected methods:
|
||||
return
|
||||
|
|
@ -1965,6 +1999,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_38(self):
|
||||
|
||||
print("@@@38")
|
||||
# mixed const / non-const reference and events
|
||||
ec = pya.E.ic()
|
||||
self.assertEqual(ec.is_const_object(), True)
|
||||
|
|
@ -1996,6 +2031,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_39(self):
|
||||
|
||||
print("@@@39")
|
||||
# mixed const / non-const reference and events
|
||||
fc = pya.F.ic()
|
||||
self.assertEqual(fc.is_const_object(), True)
|
||||
|
|
@ -2026,6 +2062,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_40(self):
|
||||
|
||||
print("@@@40")
|
||||
# optional arguments
|
||||
g = pya.G()
|
||||
|
||||
|
|
@ -2109,6 +2146,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_41(self):
|
||||
|
||||
print("@@@41")
|
||||
# maps
|
||||
b = pya.B()
|
||||
|
||||
|
|
@ -2170,6 +2208,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_42(self):
|
||||
|
||||
print("@@@42")
|
||||
# virtual functions and sub-classes
|
||||
z = pya.Z()
|
||||
self.assertEqual(z.f(None), "(nil)")
|
||||
|
|
@ -2198,6 +2237,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
def test_50(self):
|
||||
|
||||
print("@@@50")
|
||||
# advanced containers and out parameters
|
||||
|
||||
b = pya.B()
|
||||
|
|
@ -2372,6 +2412,7 @@ class BasicTest(unittest.TestCase):
|
|||
self.assertEqual(b.qhash_is(), {})
|
||||
|
||||
def test_51(self):
|
||||
print("@@@51")
|
||||
|
||||
# new subclass and child class declarations
|
||||
y2 = pya.Y2()
|
||||
|
|
@ -2389,6 +2430,7 @@ class BasicTest(unittest.TestCase):
|
|||
self.assertEqual(isinstance(y4, pya.X), False)
|
||||
|
||||
def test_60(self):
|
||||
print("@@@60")
|
||||
|
||||
class SignalCollector(object):
|
||||
|
||||
|
|
@ -2482,6 +2524,7 @@ class BasicTest(unittest.TestCase):
|
|||
self.assertEqual(sc.got_s2_2, None)
|
||||
|
||||
def test_61(self):
|
||||
print("@@@61")
|
||||
|
||||
class SignalCollector(object):
|
||||
|
||||
|
|
@ -2565,6 +2608,7 @@ class BasicTest(unittest.TestCase):
|
|||
self.assertEqual(sc.got_s0b, 0)
|
||||
|
||||
def test_70(self):
|
||||
print("@@@70")
|
||||
|
||||
class SignalCollector(object):
|
||||
|
||||
|
|
@ -2613,6 +2657,7 @@ class BasicTest(unittest.TestCase):
|
|||
self.assertEqual(sc.got_s2_2.tag, 111)
|
||||
|
||||
def test_71(self):
|
||||
print("@@@71")
|
||||
|
||||
class SignalCollector(object):
|
||||
|
||||
|
|
@ -2697,6 +2742,7 @@ class BasicTest(unittest.TestCase):
|
|||
|
||||
# Custom factory implemented in Python
|
||||
def test_80(self):
|
||||
print("@@@80")
|
||||
gc = pya.GObject.g_inst_count()
|
||||
gf = PyGFactory()
|
||||
go = pya.GFactory.create_f(gf, 17)
|
||||
|
|
@ -2706,6 +2752,7 @@ class BasicTest(unittest.TestCase):
|
|||
go = None
|
||||
self.assertEqual(pya.GObject.g_inst_count(), gc)
|
||||
|
||||
print("@@@2")
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestSuite()
|
||||
|
|
@ -2715,4 +2762,5 @@ if __name__ == '__main__':
|
|||
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
print("@@@3")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue