Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into ctypes_patch_no_pri…
Browse files Browse the repository at this point in the history
…vate_api
  • Loading branch information
freakboy3742 committed Jun 30, 2018
2 parents 14a2477 + cdaca74 commit dc16fc4
Show file tree
Hide file tree
Showing 18 changed files with 2,010 additions and 1,692 deletions.
1 change: 1 addition & 0 deletions docs/background/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ plans for the future? That's what you'll find here!

faq
community
success
releases
roadmap
4 changes: 4 additions & 0 deletions docs/background/releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Release History
* Removed automatic conversion of ``NSString`` objects to ``str`` when returned from Objective-C methods. This feature made it difficult to call Objective-C methods on ``NSString`` objects, because there was no easy way to prevent the automatic conversion.
* In most cases, this change will not affect existing code, because ``NSString`` objects now support operations similar to ``str``.
* If an actual ``str`` object is required, the ``NSString`` object can be wrapped in a ``str`` call to convert it.
* Added support for ``objc_property`` with non-object types.
* Added public ``get_ivar`` and ``set_ivar`` functions for manipulating ivars.
* Changed the implementation of ``objc_property`` to use ivars instead of Python attributes for storage. This fixes name conflicts in some situations.
* Fixed ``objc_property`` setters on non-macOS platforms. (cculianu)
* Fixed various bugs in the collection ``ObjCInstance`` subclasses:
* Fixed getting/setting/deleting items or slices with indices lower than ``-len(obj)``. Previously this crashed Python, now an ``IndexError`` is raised.
* Fixed slices with step size 0. Previously they were ignored and 1 was incorrectly used as the step size, now an ``IndexError`` is raised.
Expand Down
6 changes: 6 additions & 0 deletions docs/background/success.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Success Stories
===============

Want to see examples of Rubicon in use? Here's some:

* `Travel Tips <https://itunes.apple.com/au/app/travel-tips/id1336372310>`_ is an app in the iOS App Store that uses Rubicon to access the iOS UIKit libraries.
6 changes: 3 additions & 3 deletions docs/how-to/protocols.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ similar to how classes can be looked up using ``ObjCClass``:
>>> NSCopying = ObjCProtocol('NSCopying')
>>> NSCopying
<rubicon.objc.runtime.ObjCProtocol: NSCopying at 0x7fff76543210>
<rubicon.objc.api.ObjCProtocol: NSCopying at 0x7fff76543210>
The ``isinstance`` function can be used to check whether an object conforms to
a protocol:
Expand Down Expand Up @@ -67,9 +67,9 @@ We can now use our class. The ``copy`` method (which uses our implemented
>>> ua = UserAccount.alloc().initWithUsername_emailAddress_(at('person'), at('[email protected]'))
>>> ua
<rubicon.objc.runtime.ObjCInstance 0x106543210: UserAccount at 0x106543220: <UserAccount: 0x106543220>>
<rubicon.objc.api.ObjCInstance 0x106543210: UserAccount at 0x106543220: <UserAccount: 0x106543220>>
>>> ua.copy()
<rubicon.objc.runtime.ObjCInstance 0x106543210: UserAccount at 0x106543220: <UserAccount: 0x106543220>>
<rubicon.objc.api.ObjCInstance 0x106543210: UserAccount at 0x106543220: <UserAccount: 0x106543220>>
And we can check that the class conforms to the protocol:

Expand Down
166 changes: 104 additions & 62 deletions docs/how-to/type-mapping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,94 +29,136 @@ Primitives
If a Python value needs to be passed in as a primitive, Rubicon will wrap the
primitive:

===== =============================================================
Value C primitive
===== ============================================================
bool 8 bit integer (although it can only hold 2 values - 0 and 1)
int 32 bit integer
float double precision floating point
===== ============================================================
============== ============================================================
Value C primitive
============== ============================================================
:class:`bool` 8 bit integer (although it can only hold 2 values - 0 and 1)
:class:`int` 32 bit integer
:class:`float` double precision floating point
============== ============================================================

If a Python value needs to be passed in as an object, Rubicon will wrap the
primitive in an object:

===== =================
Value Objective C type
===== =================
bool NSNumber (bool)
int NSNumber (long)
float NSNumber (double)
===== =================
============== ==========================
Value Objective C type
============== ==========================
:class:`bool` :class:`NSNumber` (bool)
:class:`int` :class:`NSNumber` (long)
:class:`float` :class:`NSNumber` (double)
============== ==========================

If you're declaring a method and need to annotate the type of an argument, the
Python type name can be used as the annotation type. You can also use any of
the `ctypes` primitive types. Rubicon also provides type definitions for common
Objective-C typedefs, like `NSInteger`, `CGFloat`, and so on.
the ``ctypes`` primitive types. Rubicon also provides type definitions for common
Objective-C typedefs, like :class:`NSInteger`, :class:`CGFloat`, and so on.

Strings
-------

If a method calls for an `NSString` argument, you can provide a Python `str`
for that argument. Rubicon will construct an `NSString` instance from the data
in the `str` provided, and pass that value for the argument.

If a method returns an `NSString`, the return value will be a wrapped
`ObjCStrInstance` type. This type implements a `str`-like interface, wrapped
around the underlying `NSString` data. This means you can treat the return
value as if it were a string - slicing it, concatenating it with other strings,
comparing it, and so on.

Note that `ObjCStrInstance` objects behave slightly differently than Python
`str` objects in some cases. For technical reasons, `ObjCStrInstance` objects
are not hashable, which means they cannot be used as `dict` keys (but they
*can* be used as `NSDictionary` keys). `ObjCStrInstance` also handles Unicode
code points above U+FFFF differently than Python `str`, because the underlying
`NSString` is based on UTF-16.

At the moment `ObjCStrInstance` does not yet support many methods that are
available on `str`. More methods will be implemented in the future, such as
`replace` and `split`. However some methods will likely never be available on
`ObjCStrInstance` as they would be too complex to reimplement, such as `format`
and `encode`. If you need to use a method that `ObjCStrInstance` doesn't
support, you can use `str(nsstring)` to convert it to `str`.
If a method calls for an :class:`NSString` argument, you can provide a Python
:class:`str` for that argument. Rubicon will construct an :class:`NSString`
instance from the data in the :class:`str` provided, and pass that value for
the argument.

If a method returns an :class:`NSString`, the return value will be a wrapped
:class:`ObjCStrInstance` type. This type implements a :class:`str`-like
interface, wrapped around the underlying :class:`NSString` data. This means
you can treat the return value as if it were a string - slicing it,
concatenating it with other strings, comparing it, and so on::

# Call an Objective C method that returns a string.
# We're using NSBundle to give us a string version of a path
>>> NSBundle.mainBundle.bundlePath
<rubicon.objc.collections.ObjCStrInstance 0x114a94d68: __NSCFString at 0x7fec8ba7fbd0: /Users/brutus/path/to/somewhere>

# Slice the Objective C string
>>> NSBundle.mainBundle.bundlePath[:14]
<rubicon.objc.collections.ObjCStrInstance 0x114aa80f0: __NSCFString at 0x7fec8ba7fbd0: /Users/brutus/>

Note that :class:`ObjCStrInstance` objects behave slightly differently than
Python :class:`str` objects in some cases. For technical reasons,
:class:`ObjCStrInstance` objects are not hashable, which means they cannot be
used as :class:`dict` keys (but they *can* be used as :class:`NSDictionary`
keys). :class:`ObjCStrInstance` also handles Unicode code points above
``U+FFFF`` differently than Python :class:`str`, because the underlying
:class:`NSString` is based on UTF-16.

If you have an :class:`ObjCStrInstance` instance, and you need to pass that
instance to a method that does a specific typecheck for :class:`str`, you can
use ``str(nsstring)`` to convert the :class:`ObjCStrInstance` instance to
:class:`str`::

# Convert the Objective C string to a Python string.
>>> str(NSBundle.mainBundle.bundlePath)
'/Users/rkm/projects/beeware/venv3.6/bin'

Conversely, if you have a :class:`str`, and you specifically require a
:class:`ObjCStrInstance` instance, you can use the :meth:`at()` method to
convert the Python instance to an :class:`ObjCStrInstance`.

>>> from rubicon.objc import at
# Create a Python string
>>> py_str = 'hello world'

# Convert to an Objective C string
>>> at(py_str)
<rubicon.objc.collections.ObjCStrInstance 0x114a94e48: __NSCFString at 0x7fec8ba7fc10: hello world>

:class:`ObjCStrInstance` implements all the utility methods that are available
on :class:`str`, such as ``replace`` and ``split``. When these methods return
a string, the implementation may return Python :class:`str` or
:class:`ObjCStrInstance` instances. If you need to use the return value from
these methods, you should always use ``str()`` to ensure you have a Python
string::

# Is the path comprised of all lowercase letters? (Hint: it isn't)
>>> NSBundle.mainBundle.bundlePath.islower()
False

# Convert string to lower case; use str() to ensure we get a Python string.
>>> str(NSBundle.mainBundle.bundlePath.lower())
'/users/rkm/projects/beeware/venv3.6/bin'


Lists
-----

If a method calls for an `NSArray` or `NSMutableArray` argument, you can
provide a Python `list` for that argument. Rubicon will construct an
`NSMutableArray` instance from the data in the `list` provided, and pass that
value for the argument.
If a method calls for an :class:`NSArray` or :class:`NSMutableArray` argument,
you can provide a Python :class:`list` for that argument. Rubicon will
construct an :class:`NSMutableArray` instance from the data in the
:class:`list` provided, and pass that value for the argument.

If a method returns an `NSArray` or `NSMutableArray`, the return value will be
a wrapped `ObjCListInstance` type. This type implements a `list`-like
interface, wrapped around the underlying `NSArray` data. This means you can
treat the return value as if it were a list - iterating over values, retrieving
objects by index, and so on.
If a method returns an :class:`NSArray` or :class:`NSMutableArray`, the return
value will be a wrapped :class:`ObjCListInstance` type. This type implements a
:class:`list`-like interface, wrapped around the underlying :class:`NSArray`
data. This means you can treat the return value as if it were a list -
iterating over values, retrieving objects by index, and so on.

Dictionaries
------------

If a method calls for an `NSDictionary` or `NSMutableDictionary` argument, you
can provide a Python `dict`. Rubicon will construct an `NSMutableDictionary`
instance from the data in the `dict` provided, and pass that value for the
argument.
If a method calls for an :class:`NSDictionary` or :class:`NSMutableDictionary`
argument, you can provide a Python :class:`dict`. Rubicon will construct an
:class:`NSMutableDictionary` instance from the data in the :class:`dict`
provided, and pass that value for the argument.

If a method returns an `NSDictionary` or `NSMutableDictionary`, the return
value will be a wrapped `ObjCDictInstance` type. This type implements a
`dict`-like interface, wrapped around the underlying `NSDictionary` data. This
means you can treat the return value as if it were a dict - iterating over
keys, values or items, retrieving objects by key, and so on.
If a method returns an :class:`NSDictionary` or :class:`NSMutableDictionary`,
the return value will be a wrapped :class:`ObjCDictInstance` type. This type
implements a :class:`dict`-like interface, wrapped around the underlying
:class:`NSDictionary` data. This means you can treat the return value as if it
were a dict - iterating over keys, values or items, retrieving objects by key,
and so on.


`NSPoint`, `NSSize`, and `NSRect`
---------------------------------
:class:`NSPoint`, :class:`NSSize`, and :class:`NSRect`
------------------------------------------------------

On instances of an Objective C structure, each field is exposed as a Python
attribute. For example, if you create an instance of an `NSSize` object you can
access its width and height by calling `NSSize.width`.
attribute. For example, if you create an instance of an :class:`NSSize` object
you can access its width and height by calling :meth:`NSSize.width`.

When you need to pass an Objective C structure to an Objective C method,
you can pass a tuple instead. For example, if you pass (10.0, 5.1) where a
`NSSize` is expected, it will be converted automatically in the appropriate
:class:`NSSize` is expected, it will be converted automatically in the appropriate
width, height for the structure.
4 changes: 2 additions & 2 deletions docs/tutorial/tutorial-1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ equivalents, `__str__()` and `__repr__()`, respectively::
'http://pybee.org/contributing/how/first-time/'

>>> repr(absolute)
'<rubicon.objc.runtime.ObjCInstance 0x1114a3cf8: NSURL at 0x7fb2abdd0b20: http://pybee.org/contributing/>'
'<rubicon.objc.api.ObjCInstance 0x1114a3cf8: NSURL at 0x7fb2abdd0b20: http://pybee.org/contributing/>'

>>> print(absolute)
<rubicon.objc.runtime.ObjCInstance 0x1114a3cf8: NSURL at 0x7fb2abdd0b20: http://pybee.org/contributing/>
<rubicon.objc.api.ObjCInstance 0x1114a3cf8: NSURL at 0x7fb2abdd0b20: http://pybee.org/contributing/>

Time to take over the world!
----------------------------
Expand Down
23 changes: 11 additions & 12 deletions rubicon/objc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
__version__ = '0.2.10'

# Import commonly used submodules right away.
# The first two imports are only included for clarity. They are not strictly necessary, because the from-imports below
# The first few imports are only included for clarity. They are not strictly necessary, because the from-imports below
# also import the types and runtime modules and implicitly add them to the rubicon.objc namespace.
from . import types # noqa: F401
from . import runtime # noqa: F401
from . import api # noqa: F401
# The import of collections is important, however. The classes from collections are not meant to be used directly,
# instead they are registered with the runtime module (using the for_objcclass decorator) so they are used in place of
# ObjCInstance when representing Foundation collections in Python. If this module is not imported, the registration
# will not take place, and Foundation collections will not support the expected methods/operators in Python!
from . import collections # noqa: F401

from .runtime import ( # noqa: F401
IMP, SEL, Block, Class, Ivar, Method, NSArray, NSDictionary, NSMutableArray, NSMutableDictionary, NSObject,
NSObjectProtocol, ObjCBlock, ObjCClass, ObjCInstance, ObjCMetaClass, ObjCProtocol, at, ns_from_py,
objc_classmethod, objc_const, objc_id, objc_ivar, objc_method, objc_property, objc_property_t, objc_rawmethod,
py_from_ns, send_message, send_super,
)
from .types import ( # noqa: F401
CFIndex, CFRange, CGFloat, CGGlyph, CGPoint, CGPointMake, CGRect,
CGRectMake, CGSize, CGSizeMake, NSEdgeInsets, NSEdgeInsetsMake, NSInteger,
NSMakePoint, NSMakeRect, NSMakeSize, NSPoint, NSRange, NSRect, NSSize,
NSTimeInterval, NSUInteger, NSZeroPoint, UIEdgeInsets, UIEdgeInsetsMake,
UIEdgeInsetsZero, UniChar, unichar,
CFIndex, CFRange, CGFloat, CGGlyph, CGPoint, CGPointMake, CGRect, CGRectMake, CGSize, CGSizeMake, NSEdgeInsets,
NSEdgeInsetsMake, NSInteger, NSMakePoint, NSMakeRect, NSMakeSize, NSPoint, NSRange, NSRect, NSSize, NSTimeInterval,
NSUInteger, NSZeroPoint, UIEdgeInsets, UIEdgeInsetsMake, UIEdgeInsetsZero, UniChar, unichar,
)
from .runtime import SEL, send_message, send_super # noqa: F401
from .api import ( # noqa: F401
Block, NSArray, NSDictionary, NSMutableArray, NSMutableDictionary, NSObject, NSObjectProtocol, ObjCBlock,
ObjCClass, ObjCInstance, ObjCMetaClass, ObjCProtocol, at, ns_from_py, objc_classmethod, objc_const, objc_ivar,
objc_method, objc_property, objc_rawmethod, py_from_ns,
)
Loading

0 comments on commit dc16fc4

Please sign in to comment.