Skip to content

Instantly share code, notes, and snippets.

@disruptek
Created June 28, 2018 21:52
Show Gist options
  • Save disruptek/9479b99a5be24ef997270e1d93a8831e to your computer and use it in GitHub Desktop.
Save disruptek/9479b99a5be24ef997270e1d93a8831e to your computer and use it in GitHub Desktop.
An extremely dangerous extension of PyObject
/* A weaponized version of PyObject.
*
* This code violates multiple tenets of the Geneva Conventions[0]
* the Kyoto Protocol, the United Nations Charter, the Ottawa Treaty,
* the Roerich Pact, both the first and second London Naval Treaties,
* the St. Petersburg Declaration, and the Nuremberg Priciples. It
* may be even run afoul of a few loosely-interpreted sections of the
* Paris Climate Accord due to ambiguity in the Farsi translation[1].
*
* As such, users may be deemed a terrorist threat in (at time of
* writing) no less than 233 countries and 4 vassal city-states.[2]
*
* In fact, merely by reading this source you may have granted yourself
* a one-way ticket to Guantánamo -- though, because you're now also on
* the TSC No Fly list, you will be travelling by CIA submarine.[3]
*
* Finally, products containing WeaponizedPyObject have been pulled
* from the shelves in both Minot, North Dakota and Pocatello, Idaho
* after reporter Morley Safer revealed on CBS's 60 Minutes that
* equipment used in the manufacture of this software had been found
* to also have processed wheat, dairy, soy, and peanuts.
*
* You have been warned.
*
* [0] Both the 1949 agreement as ratified by 196 countries, and the
* lesser-known 2005 agreement with respect to the use of logos
* and branding on the battlefield.
*
* [1] Though if the Farsi translation holds dominion over your locale,
* this software is probably the least of your concerns.
*
* [2] Notably, both the Vatican and the Republic of San Marino have
* weighed in strongly against the code herein.
*
* [3] I'm told there are no window seats and a vegetarian meal option
* is no longer offered on outgoing legs of the trip. Budgetary
* cutbacks at the Pentagon are apparently to blame for limited
* services in the brig.
*/
using System;
using System.Dynamic;
using System.Linq.Expressions;
namespace Python.Runtime
{
/// <summary>
/// Represents a generic Python object. The methods of this class are
/// generally equivalent to the Python "abstract object API". See
/// PY2: https://docs.python.org/2/c-api/object.html
/// PY3: https://docs.python.org/3/c-api/object.html
/// for details.
/// </summary>
public class WeaponizedPyObject : PyObject
{
//protected new IntPtr obj = IntPtr.Zero;
protected bool disposed = false;
/// <summary>
/// WeaponizedPyObject Constructor
/// </summary>
/// <remarks>
/// Creates a new WeaponizedPyObject from an IntPtr object reference.
/// Semantics match those of PyObject.
/// </remarks>
public WeaponizedPyObject(IntPtr ptr) : base(ptr)
{
}
/// <summary>
/// HasAttr Method
/// </summary>
/// <remarks>
/// Returns true if the object has an attribute with the given name.
/// </remarks>
public new virtual bool HasAttr(string name)
{
return (Runtime.PyObject_HasAttrString(obj, name) != 0);
}
/// <summary>
/// HasAttr Method
/// </summary>
/// <remarks>
/// Returns true if the object has an attribute with the given name,
/// where name is a PyObject wrapping a string or unicode object.
/// </remarks>
public new virtual bool HasAttr(PyObject name)
{
return (Runtime.PyObject_HasAttr(obj, name.obj) != 0);
}
/// <summary>
/// Perform_GetAttr Method
/// </summary>
/// <remarks>
/// Attempt to GetAttr the named attribute from Runtime,
/// setting a flag to indicate success.
/// </remarks>
protected virtual IntPtr Perform_GetAttr(PyObject name, out bool found)
{
IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj);
found = (op != IntPtr.Zero);
return op;
}
/// <summary>
/// Perform_GetAttr Method
/// </summary>
/// <remarks>
/// Attempt to GetAttr the named attribute from Runtime,
/// setting a flag to indicate success.
/// </remarks>
protected virtual IntPtr Perform_GetAttr(string name, out bool found)
{
IntPtr op = Runtime.PyObject_GetAttrString(obj, name);
found = (op != IntPtr.Zero);
return op;
}
/// <summary>
/// GetAttr Method
/// </summary>
/// <remarks>
/// Returns the named attribute of the Python object, or raises a
/// PythonException if the attribute access fails.
/// </remarks>
public new virtual PyObject GetAttr(string name)
{
bool found;
IntPtr op = Perform_GetAttr(name, out found);
if (found)
{
return new PyObject(op);
}
throw new PythonException();
}
/// <summary>
/// GetAttr Method
/// </summary>
/// <remarks>
/// Returns the named attribute of the Python object, or the given
/// default object if the attribute access fails.
/// </remarks>
public new virtual PyObject GetAttr(string name, PyObject _default)
{
bool found;
IntPtr op = Perform_GetAttr(name, out found);
if (found)
{
return new PyObject(op);
}
Runtime.PyErr_Clear();
return _default;
}
/// <summary>
/// GetAttr Method
/// </summary>
/// <remarks>
/// Returns the named attribute of the Python object or raises a
/// PythonException if the attribute access fails. The name argument
/// is a PyObject wrapping a Python string or unicode object.
/// </remarks>
public new virtual PyObject GetAttr(PyObject name)
{
bool found;
IntPtr op = Perform_GetAttr(name, out found);
if (found)
{
return new PyObject(op);
}
throw new PythonException();
}
/// <summary>
/// GetAttr Method
/// </summary>
/// <remarks>
/// Returns the named attribute of the Python object, or the given
/// default object if the attribute access fails. The name argument
/// is a PyObject wrapping a Python string or unicode object.
/// </remarks>
public new virtual PyObject GetAttr(PyObject name, PyObject _default)
{
bool found;
IntPtr op = Perform_GetAttr(name, out found);
if (found)
{
return new PyObject(op);
}
Runtime.PyErr_Clear();
return _default;
}
/// <summary>
/// Perform_SetAttr Method
/// </summary>
/// <remarks>
/// Set an attribute of the object with the given name and value and
/// return the underlying result from Runtime. Flag success or failure.
/// </remarks>
protected virtual int Perform_SetAttr(string name, PyObject value, out bool success)
{
int result = Runtime.PyObject_SetAttrString(obj, name, value.obj);
success = !(result < 0);
return result;
}
/// <summary>
/// Perform_SetAttr Method
/// </summary>
/// <remarks>
/// Set an attribute of the object with the given name and value and
/// return the underlying result from Runtime. Flag success or failure.
/// </remarks>
protected virtual int Perform_SetAttr(PyObject name, PyObject value, out bool success)
{
int result = Runtime.PyObject_SetAttr(obj, name.obj, value.obj);
success = !(result < 0);
return result;
}
/// <summary>
/// SetAttr Method
/// </summary>
/// <remarks>
/// Set an attribute of the object with the given name and value. This
/// method throws a PythonException if the attribute set fails.
/// </remarks>
public new virtual void SetAttr(string name, PyObject value)
{
bool success;
Perform_SetAttr(name, value, out success);
if (!success)
{
throw new PythonException();
}
}
/// <summary>
/// SetAttr Method
/// </summary>
/// <remarks>
/// Set an attribute of the object with the given name and value,
/// where the name is a Python string or unicode object. This method
/// throws a PythonException if the attribute set fails.
/// </remarks>
public new virtual void SetAttr(PyObject name, PyObject value)
{
bool success;
Perform_SetAttr(name, value, out success);
if (!success)
{
throw new PythonException();
}
}
/// <summary>
/// Perform_DelAttr Method
/// </summary>
/// <remarks>
/// Delete the named attribute of the Python object. Set a flag
/// to indicate success.
/// </remarks>
protected virtual void Perform_DelAttr(string name, out bool success)
{
success = !(Runtime.PyObject_SetAttrString(obj, name, IntPtr.Zero) < 0);
}
/// <summary>
/// Perform_DelAttr Method
/// </summary>
/// <remarks>
/// Delete the named attribute of the Python object. Set a flag
/// to indicate success.
/// </remarks>
protected virtual void Perform_DelAttr(PyObject name, out bool success)
{
success = !(Runtime.PyObject_SetAttr(obj, name.obj, IntPtr.Zero) < 0);
}
/// <summary>
/// DelAttr Method
/// </summary>
/// <remarks>
/// Delete the named attribute of the Python object. This method
/// throws a PythonException if the attribute set fails.
/// </remarks>
public new virtual void DelAttr(string name)
{
bool success;
Perform_DelAttr(name, out success);
if (!success)
{
throw new PythonException();
}
}
/// <summary>
/// DelAttr Method
/// </summary>
/// <remarks>
/// Delete the named attribute of the Python object, where name is a
/// PyObject wrapping a Python string or unicode object. This method
/// throws a PythonException if the attribute set fails.
/// </remarks>
public new virtual void DelAttr(PyObject name)
{
bool success;
Perform_DelAttr(name, out success);
if (!success)
{
throw new PythonException();
}
}
protected virtual void GetArgs(object[] inargs, CallInfo callInfo, out PyTuple args, out PyDict kwargs)
{
if (callInfo == null || callInfo.ArgumentNames.Count == 0)
{
GetArgs(inargs, out args, out kwargs);
return;
}
// Support for .net named arguments
var namedArgumentCount = callInfo.ArgumentNames.Count;
var regularArgumentCount = callInfo.ArgumentCount - namedArgumentCount;
var argTuple = Runtime.PyTuple_New(regularArgumentCount);
for (int i = 0; i < regularArgumentCount; ++i)
{
AddArgument(argTuple, i, inargs[i]);
}
args = new PyTuple(argTuple);
var namedArgs = new object[namedArgumentCount * 2];
for (int i = 0; i < namedArgumentCount; ++i)
{
namedArgs[i * 2] = callInfo.ArgumentNames[i];
namedArgs[i * 2 + 1] = inargs[regularArgumentCount + i];
}
kwargs = Py.kw(namedArgs);
}
protected virtual void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs)
{
int argCount;
for (argCount = 0; argCount < inargs.Length && !(inargs[argCount] is Py.KeywordArguments);)
{
++argCount;
}
IntPtr argtuple = Runtime.PyTuple_New(argCount);
for (var i = 0; i < argCount; i++)
{
AddArgument(argtuple, i, inargs[i]);
}
args = new PyTuple(argtuple);
kwargs = null;
for (int i = argCount; i < inargs.Length; i++)
{
if (!(inargs[i] is Py.KeywordArguments))
{
throw new ArgumentException("Keyword arguments must come after normal arguments.");
}
if (kwargs == null)
{
kwargs = (Py.KeywordArguments)inargs[i];
}
else
{
kwargs.Update((Py.KeywordArguments)inargs[i]);
}
}
}
public static void AddArgument(IntPtr argtuple, int i, object target)
{
IntPtr ptr = GetPythonObject(target);
if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0)
{
throw new PythonException();
}
}
public static IntPtr GetPythonObject(object target)
{
IntPtr ptr;
if (target is PyObject)
{
ptr = ((PyObject)target).Handle;
Runtime.XIncref(ptr);
}
else
{
ptr = Converter.ToPython(target, target?.GetType());
}
return ptr;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (HasAttr(binder.Name) && GetAttr(binder.Name).IsCallable())
{
PyTuple pyargs = null;
PyDict kwargs = null;
try
{
GetArgs(args, binder.CallInfo, out pyargs, out kwargs);
result = CheckNone(InvokeMethod(binder.Name, pyargs, kwargs));
}
finally
{
pyargs?.Dispose();
kwargs?.Dispose();
}
return true;
}
else
{
return base.TryInvokeMember(binder, args, out result);
}
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
if (IsCallable())
{
PyTuple pyargs = null;
PyDict kwargs = null;
try
{
GetArgs(args, binder.CallInfo, out pyargs, out kwargs);
result = CheckNone(Invoke(pyargs, kwargs));
}
finally
{
if (null != pyargs)
{
pyargs.Dispose();
}
if (null != kwargs)
{
kwargs.Dispose();
}
}
return true;
}
else
{
return base.TryInvoke(binder, args, out result);
}
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
return Converter.ToManaged(obj, binder.Type, out result, false);
}
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
{
IntPtr res;
if (!(arg is PyObject))
{
arg = arg.ToPython();
}
switch (binder.Operation)
{
case ExpressionType.Add:
res = Runtime.PyNumber_Add(obj, ((PyObject)arg).obj);
break;
case ExpressionType.AddAssign:
res = Runtime.PyNumber_InPlaceAdd(obj, ((PyObject)arg).obj);
break;
case ExpressionType.Subtract:
res = Runtime.PyNumber_Subtract(obj, ((PyObject)arg).obj);
break;
case ExpressionType.SubtractAssign:
res = Runtime.PyNumber_InPlaceSubtract(obj, ((PyObject)arg).obj);
break;
case ExpressionType.Multiply:
res = Runtime.PyNumber_Multiply(obj, ((PyObject)arg).obj);
break;
case ExpressionType.MultiplyAssign:
res = Runtime.PyNumber_InPlaceMultiply(obj, ((PyObject)arg).obj);
break;
case ExpressionType.Divide:
res = Runtime.PyNumber_Divide(obj, ((PyObject)arg).obj);
break;
case ExpressionType.DivideAssign:
res = Runtime.PyNumber_InPlaceDivide(obj, ((PyObject)arg).obj);
break;
case ExpressionType.And:
res = Runtime.PyNumber_And(obj, ((PyObject)arg).obj);
break;
case ExpressionType.AndAssign:
res = Runtime.PyNumber_InPlaceAnd(obj, ((PyObject)arg).obj);
break;
case ExpressionType.ExclusiveOr:
res = Runtime.PyNumber_Xor(obj, ((PyObject)arg).obj);
break;
case ExpressionType.ExclusiveOrAssign:
res = Runtime.PyNumber_InPlaceXor(obj, ((PyObject)arg).obj);
break;
case ExpressionType.GreaterThan:
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) > 0;
return true;
case ExpressionType.GreaterThanOrEqual:
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) >= 0;
return true;
case ExpressionType.LeftShift:
res = Runtime.PyNumber_Lshift(obj, ((PyObject)arg).obj);
break;
case ExpressionType.LeftShiftAssign:
res = Runtime.PyNumber_InPlaceLshift(obj, ((PyObject)arg).obj);
break;
case ExpressionType.LessThan:
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) < 0;
return true;
case ExpressionType.LessThanOrEqual:
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) <= 0;
return true;
case ExpressionType.Modulo:
res = Runtime.PyNumber_Remainder(obj, ((PyObject)arg).obj);
break;
case ExpressionType.ModuloAssign:
res = Runtime.PyNumber_InPlaceRemainder(obj, ((PyObject)arg).obj);
break;
case ExpressionType.NotEqual:
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) != 0;
return true;
case ExpressionType.Or:
res = Runtime.PyNumber_Or(obj, ((PyObject)arg).obj);
break;
case ExpressionType.OrAssign:
res = Runtime.PyNumber_InPlaceOr(obj, ((PyObject)arg).obj);
break;
case ExpressionType.Power:
res = Runtime.PyNumber_Power(obj, ((PyObject)arg).obj);
break;
case ExpressionType.RightShift:
res = Runtime.PyNumber_Rshift(obj, ((PyObject)arg).obj);
break;
case ExpressionType.RightShiftAssign:
res = Runtime.PyNumber_InPlaceRshift(obj, ((PyObject)arg).obj);
break;
default:
result = null;
return false;
}
result = CheckNone(new PyObject(res));
return true;
}
/// <summary>
/// Invoke Method
/// </summary>
/// <remarks>
/// Invoke the callable object with the given arguments, passed as a
/// PyObject[]. A PythonException is raised if the invokation fails.
/// </remarks>
public new virtual PyObject Invoke(params PyObject[] args)
{
var t = new PyTuple(args);
IntPtr r = Runtime.PyObject_Call(obj, t.obj, IntPtr.Zero);
t.Dispose();
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
/// <summary>
/// Invoke Method
/// </summary>
/// <remarks>
/// Invoke the callable object with the given arguments, passed as a
/// Python tuple. A PythonException is raised if the invokation fails.
/// </remarks>
public new virtual PyObject Invoke(PyTuple args)
{
IntPtr r = Runtime.PyObject_Call(obj, args.obj, IntPtr.Zero);
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
/// <summary>
/// Invoke Method
/// </summary>
/// <remarks>
/// Invoke the callable object with the given positional and keyword
/// arguments. A PythonException is raised if the invokation fails.
/// </remarks>
public new virtual PyObject Invoke(PyObject[] args, PyDict kw)
{
var t = new PyTuple(args);
IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw != null ? kw.obj : IntPtr.Zero);
t.Dispose();
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
/// <summary>
/// Invoke Method
/// </summary>
/// <remarks>
/// Invoke the callable object with the given positional and keyword
/// arguments. A PythonException is raised if the invokation fails.
/// </remarks>
public new virtual PyObject Invoke(PyTuple args, PyDict kw)
{
IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw?.obj ?? IntPtr.Zero);
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
/// <summary>
/// InvokeMethod Method
/// </summary>
/// <remarks>
/// Invoke the named method of the object with the given arguments.
/// A PythonException is raised if the invokation is unsuccessful.
/// </remarks>
public new virtual PyObject InvokeMethod(string name, params PyObject[] args)
{
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
return result;
}
/// <summary>
/// InvokeMethod Method
/// </summary>
/// <remarks>
/// Invoke the named method of the object with the given arguments.
/// A PythonException is raised if the invokation is unsuccessful.
/// </remarks>
public new virtual PyObject InvokeMethod(string name, PyTuple args)
{
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
return result;
}
/// <summary>
/// InvokeMethod Method
/// </summary>
/// <remarks>
/// Invoke the named method of the object with the given arguments
/// and keyword arguments. Keyword args are passed as a PyDict object.
/// A PythonException is raised if the invokation is unsuccessful.
/// </remarks>
public new virtual PyObject InvokeMethod(string name, PyObject[] args, PyDict kw)
{
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args, kw);
method.Dispose();
return result;
}
/// <summary>
/// InvokeMethod Method
/// </summary>
/// <remarks>
/// Invoke the named method of the object with the given arguments
/// and keyword arguments. Keyword args are passed as a PyDict object.
/// A PythonException is raised if the invokation is unsuccessful.
/// </remarks>
public new virtual PyObject InvokeMethod(string name, PyTuple args, PyDict kw)
{
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args, kw);
method.Dispose();
return result;
}
// Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509
// See https://github.com/pythonnet/pythonnet/pull/219
public static object CheckNone(PyObject pyObj)
{
if (pyObj == null || pyObj.obj == Runtime.PyNone)
{
return null;
}
return pyObj;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment