<commit_before>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file nodePath_ext.cxx
 * @author rdb
 * @date 2013-12-09
 */

#include "nodePath_ext.h"
#include "typedWritable_ext.h"
#include "shaderInput_ext.h"
#include "shaderAttrib.h"

#ifdef HAVE_PYTHON

#ifndef CPPPARSER
extern struct Dtool_PyTypedObject Dtool_BamWriter;
extern struct Dtool_PyTypedObject Dtool_BamReader;
#ifdef STDFLOAT_DOUBLE
extern struct Dtool_PyTypedObject Dtool_LPoint3d;
#else
extern struct Dtool_PyTypedObject Dtool_LPoint3f;
#endif
extern struct Dtool_PyTypedObject Dtool_NodePath;
#endif  // CPPPARSER

/**
 * A special Python method that is invoked by copy.copy(node).  Unlike the
 * NodePath copy constructor, this makes a duplicate copy of the underlying
 * PandaNode (but shares children, instead of copying them or omitting them).
 */
NodePath Extension<NodePath>::
__copy__() const {
  if (_this->is_empty()) {
    // Invoke the copy constructor if we have no node.
    return *_this;
  }

  // If we do have a node, duplicate it, and wrap it in a new NodePath.
  return NodePath(invoke_extension(_this->node()).__copy__());
}

/**
 * A special Python method that is invoked by copy.deepcopy(np).  This calls
 * copy_to() unless the NodePath is already present in the provided
 * dictionary.
 */
PyObject *Extension<NodePath>::
__deepcopy__(PyObject *self, PyObject *memo) const {
  extern struct Dtool_PyTypedObject Dtool_NodePath;

  // Borrowed reference.
  PyObject *dupe = PyDict_GetItem(memo, self);
  if (dupe != NULL) {
    // Already in the memo dictionary.
    Py_INCREF(dupe);
    return dupe;
  }

  NodePath *np_dupe;
  if (_this->is_empty()) {
    np_dupe = new NodePath(*_this);
  } else {
    np_dupe = new NodePath(_this->copy_to(NodePath()));
  }

  dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath,
                                true, false);
  if (PyDict_SetItem(memo, self, dupe) != 0) {
    Py_DECREF(dupe);
    return NULL;
  }

  return dupe;
}

/**
 * This special Python method is implement to provide support for the pickle
 * module.
 *
 * This hooks into the native pickle and cPickle modules, but it cannot
 * properly handle self-referential BAM objects.
 */
PyObject *Extension<NodePath>::
__reduce__(PyObject *self) const {
  return __reduce_persist__(self, NULL);
}

/**
 * This special Python method is implement to provide support for the pickle
 * module.
 *
 * This is similar to __reduce__, but it provides additional support for the
 * missing persistent-state object needed to properly support self-referential
 * BAM objects written to the pickle stream.  This hooks into the pickle and
 * cPickle modules implemented in direct/src/stdpy.
 */
PyObject *Extension<NodePath>::
__reduce_persist__(PyObject *self, PyObject *pickler) const {
  // We should return at least a 2-tuple, (Class, (args)): the necessary class
  // object whose constructor we should call (e.g.  this), and the arguments
  // necessary to reconstruct this object.

  BamWriter *writer = NULL;
  if (pickler != NULL) {
    PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
    if (py_writer == NULL) {
      // It's OK if there's no bamWriter.
      PyErr_Clear();
    } else {
      DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer);
      Py_DECREF(py_writer);
    }
  }

  // We have a non-empty NodePath.

  vector_uchar bam_stream;
  if (!_this->encode_to_bam_stream(bam_stream, writer)) {
    ostringstream stream;
    stream << "Could not bamify " << _this;
    string message = stream.str();
    PyErr_SetString(PyExc_TypeError, message.c_str());
    return NULL;
  }

  // Start by getting this class object.
  PyObject *this_class = PyObject_Type(self);
  if (this_class == NULL) {
    return NULL;
  }

  PyObject *func;
  if (writer != NULL) {
    // The modified pickle support: call the "persistent" version of this
    // function, which receives the unpickler itself as an additional
    // parameter.
    func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist");
    if (func == NULL) {
      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()");
      Py_DECREF(this_class);
      return NULL;
    }

  } else {
    // The traditional pickle support: call the non-persistent version of this
    // function.
    func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream");
    if (func == NULL) {
      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()");
      Py_DECREF(this_class);
      return NULL;
    }
  }

  // PyTuple_SET_ITEM conveniently borrows the reference it is passed.
  PyObject *args = PyTuple_New(2);
  PyTuple_SET_ITEM(args, 0, this_class);
  PyTuple_SET_ITEM(args, 1, Dtool_WrapValue(bam_stream));
<commit_msg>Fix support for pickling NodePaths (broken in 1b1d80c)<commit_after>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) <NAME>.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file nodePath_ext.cxx
 * @author rdb
 * @date 2013-12-09
 */

#include "nodePath_ext.h"
#include "typedWritable_ext.h"
#include "shaderInput_ext.h"
#include "shaderAttrib.h"

#ifdef HAVE_PYTHON

#ifndef CPPPARSER
extern struct Dtool_PyTypedObject Dtool_BamWriter;
extern struct Dtool_PyTypedObject Dtool_BamReader;
#ifdef STDFLOAT_DOUBLE
extern struct Dtool_PyTypedObject Dtool_LPoint3d;
#else
extern struct Dtool_PyTypedObject Dtool_LPoint3f;
#endif
extern struct Dtool_PyTypedObject Dtool_NodePath;
#endif  // CPPPARSER

/**
 * A special Python method that is invoked by copy.copy(node).  Unlike the
 * NodePath copy constructor, this makes a duplicate copy of the underlying
 * PandaNode (but shares children, instead of copying them or omitting them).
 */
NodePath Extension<NodePath>::
__copy__() const {
  if (_this->is_empty()) {
    // Invoke the copy constructor if we have no node.
    return *_this;
  }

  // If we do have a node, duplicate it, and wrap it in a new NodePath.
  return NodePath(invoke_extension(_this->node()).__copy__());
}

/**
 * A special Python method that is invoked by copy.deepcopy(np).  This calls
 * copy_to() unless the NodePath is already present in the provided
 * dictionary.
 */
PyObject *Extension<NodePath>::
__deepcopy__(PyObject *self, PyObject *memo) const {
  extern struct Dtool_PyTypedObject Dtool_NodePath;

  // Borrowed reference.
  PyObject *dupe = PyDict_GetItem(memo, self);
  if (dupe != NULL) {
    // Already in the memo dictionary.
    Py_INCREF(dupe);
    return dupe;
  }

  NodePath *np_dupe;
  if (_this->is_empty()) {
    np_dupe = new NodePath(*_this);
  } else {
    np_dupe = new NodePath(_this->copy_to(NodePath()));
  }

  dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath,
                                true, false);
  if (PyDict_SetItem(memo, self, dupe) != 0) {
    Py_DECREF(dupe);
    return NULL;
  }

  return dupe;
}

/**
 * This special Python method is implement to provide support for the pickle
 * module.
 *
 * This hooks into the native pickle and cPickle modules, but it cannot
 * properly handle self-referential BAM objects.
 */
PyObject *Extension<NodePath>::
__reduce__(PyObject *self) const {
  return __reduce_persist__(self, NULL);
}

/**
 * This special Python method is implement to provide support for the pickle
 * module.
 *
 * This is similar to __reduce__, but it provides additional support for the
 * missing persistent-state object needed to properly support self-referential
 * BAM objects written to the pickle stream.  This hooks into the pickle and
 * cPickle modules implemented in direct/src/stdpy.
 */
PyObject *Extension<NodePath>::
__reduce_persist__(PyObject *self, PyObject *pickler) const {
  // We should return at least a 2-tuple, (Class, (args)): the necessary class
  // object whose constructor we should call (e.g.  this), and the arguments
  // necessary to reconstruct this object.

  BamWriter *writer = NULL;
  if (pickler != NULL) {
    PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
    if (py_writer == NULL) {
      // It's OK if there's no bamWriter.
      PyErr_Clear();
    } else {
      DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer);
      Py_DECREF(py_writer);
    }
  }

  // We have a non-empty NodePath.

  vector_uchar bam_stream;
  if (!_this->encode_to_bam_stream(bam_stream, writer)) {
    ostringstream stream;
    stream << "Could not bamify " << _this;
    string message = stream.str();
    PyErr_SetString(PyExc_TypeError, message.c_str());
    return NULL;
  }

  // Start by getting this class object.
  PyObject *this_class = (PyObject *)Py_TYPE(self);
  if (this_class == NULL) {
    return NULL;
  }

  PyObject *func;
  if (writer != NULL) {
    // The modified pickle support: call the "persistent" version of this
    // function, which receives the unpickler itself as an additional
    // parameter.
    func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist");
    if (func == NULL) {
      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()");
      return NULL;
    }

  } else {
    // The traditional pickle support: call the non-persistent version of this
    // function.
    func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream");
    if (func == NULL) {
      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()");
      return NULL;
    }
  }

  // PyTuple_SET_ITEM conveniently borrows the reference it is passed.
  PyObject *args = PyTuple_New(1);
  PyTuple_SET_ITEM(args, 0, Dtool_WrapValue(bam_stream));

{'fraction_non_alphanumeric': 0.09825455971759169, 'fraction_numerical': 0.003432045499117474, 'mean_word_length': 3.842830009496676, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6794580', 'n_tokens_mistral': 3288, 'n_tokens_neox': 3261, 'n_words': 1007}