def __init__(self, local_search_paths=None, url_search_prefixes=None, auth=None, uri_search_template=None): """Instantiates a ComponentStore including the specified locations. Args: local_search_paths: A list of local directories to include in the search. url_seach_prefixes: A list of URL prefixes to include in the search. auth: Auth object for the requests library. See https://requests.readthedocs.io/en/master/user/authentication/ uri_search_template: A URI template for components, which may include {name}, {digest} and {tag} variables. """ self.local_search_paths = local_search_paths or ['.'] if uri_search_template: self.uri_search_template = URITemplate(uri_search_template) self.url_search_prefixes = url_search_prefixes or [] self._auth = auth self._component_file_name = 'component.yaml' self._digests_subpath = 'versions/sha256' self._tags_subpath = 'versions/tags' cache_base_dir = Path(tempfile.gettempdir()) / '.kfp_components' self._git_blob_hash_to_data_db = KeyValueStore( cache_dir=cache_base_dir / 'git_blob_hash_to_data') self._url_to_info_db = KeyValueStore(cache_dir=cache_base_dir / 'url_to_info')
def test_default_value(self): uri = 'https://api.github.com/user{/user=sigmavirus24}' t = URITemplate(uri) self.assertEqual(t.expand(), 'https://api.github.com/user/sigmavirus24') self.assertEqual(t.expand(user='******'), 'https://api.github.com/user/lukasa')
def get_sentences(self, request): expansions = { 'count': request.get('count', None), 'max-words': request.get('max_words', None) } if request.get('feature', None) is not None: template = '/sentence{/feature}{?count,max-words}' expansions.update(feature=request['feature']) elif request.get('word', None) is not None: if request.get('pos', None) is not None: template = '/sentence/word{/word}/pos{/pos}{?count,max-words}' expansions.update(word=request['word'], pos=request['pos']) else: template = '/sentence/word{/word}{?count,max-words}' expansions.update(word=request['word']) else: raise NotImplementedError() endpoint = URITemplate(self._api_path + template) url = endpoint.expand(expansions) return self.client.get(url, headers=self.headers)
def send(req): criteria = [] for key, value in iteritems(req.get('criteria', {})): criteria.append(key) criteria.append(value) url_tmpl = URITemplate('{protocol}://{host}/api{/series}{/season}{/round}' '{/criteria*}{/resource}{/id}.json{?limit,offset}') url = url_tmpl.expand(req, criteria=criteria) res = requests.get(url) res.raise_for_status() res_data = res.json() # TODO: Format responses to target relevant data, and coerce numeric values to numbers # something like: # {limit, offset, total, url, result: []} # Remove all the ['MRData']['XXXXTable']['Races'][0] nonsense # # Might want a raw=True flag or something to be able to get around the formatting # if you want the full response for any reason. # # return Formatter(res_data, req.get('resource')) # return res_data return formatter(res_data, req)
def test_resource_class_create(): rec_type = RecordTypeFactory("ResourceTest", RecordSchema) assert rec_type.resource_cls assert rec_type.resource_config_cls assert rec_type.resource_cls.__name__ == "ResourceTestResource" assert ( rec_type.resource_config_cls.__name__ == "ResourceTestResourceConfig") assert rec_type.resource_config_cls.list_route == "/resourcetests" assert (rec_type.resource_config_cls.item_route == "/resourcetests/<pid_value>") links_schema_class = rec_type.resource_config_cls.links_config["record"] search_links_schema_class = rec_type.resource_config_cls.links_config[ "search"] assert links_schema_class.__name__ == "ResourceTestLinksSchema" assert ( search_links_schema_class.__name__ == "ResourceTestSearchLinksSchema") assert hasattr(links_schema_class, "self") assert hasattr(search_links_schema_class, "self") assert hasattr(search_links_schema_class, "prev") assert hasattr(search_links_schema_class, "next") assert (links_schema_class.self.template.uri == URITemplate( "/api/resourcetests/{pid_value}").uri) assert (search_links_schema_class.self.template.uri == URITemplate( "/api/resourcetests/{?params*}").uri) assert rec_type.resource_cls.default_config == rec_type.resource_config_cls
def __init__(self, user, session=None): super(User, self).__init__(user, session) if not self.type: self.type = 'User' #: ID of the user's image on Gravatar self.gravatar_id = user.get('gravatar_id', '') #: True -- for hire, False -- not for hire self.hireable = user.get('hireable', False) ## The number of public_gists #: Number of public gists self.public_gists = user.get('public_gists', 0) # Private information #: How much disk consumed by the user self.disk_usage = user.get('disk_usage', 0) #: Number of private repos owned by this user self.owned_private_repos = user.get('owned_private_repos', 0) #: Number of private gists owned by this user self.total_private_gists = user.get('total_private_gists', 0) #: Total number of private repos self.total_private_repos = user.get('total_private_repos', 0) #: Which plan this user is on self.plan = Plan(user.get('plan', {})) events_url = user.get('events_url', '') #: Events URL Template. Expands with ``privacy`` self.events_urlt = URITemplate(events_url) if events_url else None #: Followers URL (not a template) self.followers_url = user.get('followers_url', '') furl = user.get('following_url', '') #: Following URL Template. Expands with ``other_user`` self.following_urlt = URITemplate(furl) if furl else None gists_url = user.get('gists_url', '') #: Gists URL Template. Expands with ``gist_id`` self.gists_urlt = URITemplate(gists_url) if gists_url else None #: Organizations URL (not a template) self.organizations_url = user.get('organizations_url', '') #: Received Events URL (not a template) self.received_events_url = user.get('received_events_url', '') #: Repostories URL (not a template) self.repos_url = user.get('repos_url', '') starred_url = user.get('starred_url', '') #: Starred URL Template. Expands with ``owner`` and ``repo`` self.starred_urlt = URITemplate(starred_url) if starred_url else None #: Subscriptions URL (not a template) self.subscriptions_url = user.get('subscriptions_url', '') self._uniq = user.get('id', None)
def __init__(self, release, session=None): super(Release, self).__init__(release, session) self._api = release.get('url') #: List of :class:`Asset <Asset>` objects for this release self.assets = [Asset(i, self) for i in release.get('assets', [])] #: URL for uploaded assets self.assets_url = release.get('assets_url') #: Body of the release (the description) self.body = release.get('body') #: Date the release was created self.created_at = self._strptime(release.get('created_at')) #: Boolean whether value is True or False self.draft = release.get('draft') #: HTML URL of the release self.html_url = release.get('html_url') #: GitHub id self.id = release.get('id') #: Name given to the release self.name = release.get('name') #; Boolean whether release is a prerelease self.prerelease = release.get('prerelease') #: Date the release was published self.published_at = self._strptime(release.get('published_at')) #: Name of the tag self.tag_name = release.get('tag_name') #: "Commit" that this release targets self.target_commitish = release.get('target_commitish') upload_url = release.get('upload_url') #: URITemplate to upload an asset with self.upload_urlt = URITemplate(upload_url) if upload_url else None
def _make_package(args): # pragma: no cover """Prepare transcriptiondata from the transcription sources.""" from lingpy.sequence.sound_classes import token2class from lingpy.data import Model columns = ['LATEX', 'FEATURES', 'SOUND', 'IMAGE', 'COUNT', 'NOTE'] bipa = TranscriptionSystem('bipa') for src, rows in args.repos.iter_sources(type='td'): args.log.info('TranscriptionData {0} ...'.format(src['NAME'])) uritemplate = URITemplate( src['URITEMPLATE']) if src['URITEMPLATE'] else None out = [[ 'BIPA_GRAPHEME', 'CLTS_NAME', 'GENERATED', 'EXPLICIT', 'GRAPHEME', 'URL' ] + columns] graphemes = set() for row in rows: if row['GRAPHEME'] in graphemes: args.log.warn('skipping duplicate grapheme: {0}'.format( row['GRAPHEME'])) continue graphemes.add(row['GRAPHEME']) if not row['BIPA']: bipa_sound = bipa[row['GRAPHEME']] explicit = '' else: bipa_sound = bipa[row['BIPA']] explicit = '+' generated = '+' if bipa_sound.generated else '' if is_valid_sound(bipa_sound, bipa): bipa_grapheme = bipa_sound.s bipa_name = bipa_sound.name else: bipa_grapheme, bipa_name = '<NA>', '<NA>' url = uritemplate.expand( **row) if uritemplate else row.get('URL', '') out.append([ bipa_grapheme, bipa_name, generated, explicit, row['GRAPHEME'], url ] + [row.get(c, '') for c in columns]) found = len([o for o in out if o[0] != '<NA>']) args.log.info('... {0} of {1} graphemes found ({2:.0f}%)'.format( found, len(out), found / len(out) * 100)) with UnicodeWriter(pkg_path('transcriptiondata', '{0}.tsv'.format(src['NAME'])), delimiter='\t') as writer: writer.writerows(out) count = 0 with UnicodeWriter(pkg_path('soundclasses', 'lingpy.tsv'), delimiter='\t') as writer: writer.writerow(['CLTS_NAME', 'BIPA_GRAPHEME'] + SOUNDCLASS_SYSTEMS) for grapheme, sound in sorted(bipa.sounds.items()): if not sound.alias: writer.writerow([sound.name, grapheme] + [ token2class(grapheme, Model(cls)) for cls in SOUNDCLASS_SYSTEMS ]) count += 1 args.log.info('SoundClasses: {0} written to file.'.format(count))
def __new__(mcs, attrs, record_type_name, endpoint_route): """Object creation.""" # TODO better template building ? url_template = f"/api{endpoint_route}" + "/{?params*}" mcs.name = f"{record_type_name}SearchLinksSchema" attrs.update( { "self": Link( template=URITemplate(url_template), permission="search", params=search_link_params(0), ), "prev": Link( template=URITemplate(url_template), permission="search", params=search_link_params(-1), when=search_link_when(-1), ), "next": Link( template=URITemplate(url_template), permission="search", params=search_link_params(+1), when=search_link_when(+1), ), } ) return super().__new__(mcs, mcs.name, (), attrs)
class FileLinksSchema(Schema): """Schema for a record's links.""" self_ = Link( template=URITemplate("/api/mocks/{pid_value}/files/{key}"), permission="read", params=lambda record_file: { 'pid_value': record_file.record.pid.pid_value, 'key': record_file.key, }, data_key="self" # To avoid using self since is python reserved key ) content = Link( template=URITemplate("/api/mocks/{pid_value}/files/{key}/content"), permission="read", params=lambda record_file: { 'pid_value': record_file.record.pid.pid_value, 'key': record_file.key, }, ) commit = Link( template=URITemplate("/api/mocks/{pid_value}/files/{key}/commit"), permission="read", params=lambda record_file: { 'pid_value': record_file.record.pid.pid_value, 'key': record_file.key, }, )
class FileLinksSchema(Schema): """Schema for a record file's links.""" self_ = Link( template=URITemplate("/api/records/{pid_value}/files/{key}"), permission="read", params=lambda record_file: { 'pid_value': record_file.record.pid.pid_value, 'key': record_file.key, }, data_key="self" # To avoid using self since is python reserved key ) # TODO: Explore how to expose also the HTTP method # commit = { # "href": URITemplate("/api/records/{pid_value}/files/{key}/commit"), # "method": "POST", # } content = Link( template=URITemplate("/api/records/{pid_value}/files/{key}/content"), permission="read", params=lambda record_file: { 'pid_value': record_file.record.pid.pid_value, 'key': record_file.key, }, ) commit = Link( template=URITemplate("/api/records/{pid_value}/files/{key}/commit"), permission="read", params=lambda record_file: { 'pid_value': record_file.record.pid.pid_value, 'key': record_file.key, }, )
def test_unacceptable_route(self): # TODO decide whether failing all routes because one route has # failed is intended. with self.assertRaises(ValueError): URITemplateRouter([ URITemplate('{target}'), URITemplate('/{target}'), ])
def test_no_slash_leading_fail(self): with self.assertRaises(ValueError): check_template_leading_slash(URITemplate('{}')) with self.assertRaises(ValueError): check_template_leading_slash(URITemplate('{count}')) check_template_leading_slash(URITemplate('/{count}'))
def test_no_variables_in_uri(self): """ This test ensures that if there are no variables present, the template evaluates to itself. """ uri = 'https://api.github.com/users' t = URITemplate(uri) self.assertEqual(t.expand(), uri) self.assertEqual(t.expand(users='foo'), uri)
def __init__(self, client, url: URITemplate): self.client = client self.info_url = url.partial(info='info', resource=self.resource_name) # Setting a URIVariable to None doesn't remove it using .partial, # so reconstruct the URITemplate submit_url = url.expand(info=None, resource=self.resource_name) self.submit_url = URITemplate(submit_url + '{/id}')
def main(): print("Creating release...") # PARSE MODPACK YAML print("* Parsing modpack file") modpack = parseModpackDefinitions(MODPACK_FILE) headers = { 'Authorization': 'token ' + os.environ['GITHUB_TOKEN'], 'Content-type': 'application/json' } # POST /repos/:owner/:repo/releases print("* Creating release") with open('release.json', 'r') as f: releaseJson = f.read() if releaseJson == "": print("ERROR: releaseJson empty") else: print("releaseJson:") print(releaseJson) # payload = { # "tag_name": modpack['version'], # "name": "Release {}".format(modpack['version']), # "draft": False, # "prerelease": False, # "body": releaseJson # } createResult = requests.post('https://api.github.com/repos/soapiestwaffles/minecraft-modpack/releases', headers=headers, data=releaseJson) print(" === HTTP {}".format(createResult.status_code)) if createResult.status_code != 201: print("!!! Error creating release") print(createResult.json()) os._exit(1) createResult = createResult.json() print("* Uploading release asset") zipFilename = "soapiestwaffles-modpack.zip" template = URITemplate(createResult['upload_url']) uploadUrl = template.expand(name=zipFilename) headers = { 'Authorization': 'token ' + os.environ['GITHUB_TOKEN'], 'Content-type': 'application/octet-stream', 'Accept': 'application/json' } with open(os.path.join(OUTDIR, zipFilename), 'rb') as f: assetResult = requests.post(uploadUrl, data=f, headers=headers) print(" === HTTP {}".format(assetResult.status_code)) if assetResult.status_code != 201: print("!!! Error uploading release asset") print(assetResult.json()) os._exit(1)
def test_basic_creation_from_template(self): m = Map([ URITemplateRule(URITemplate('/'), endpoint='root'), URITemplateRule(URITemplate('/somewhere'), endpoint='elsewhere'), ]).bind('example.com') self.assertEqual(('root', {}), m.match('/')) self.assertEqual(('elsewhere', {}), m.match('/somewhere')) with self.assertRaises(NotFound): m.match('/nowhere')
class DraftLinksSchema(Schema): """Schema for a draft's links.""" self = Link(template=URITemplate("/api/mocks/{pid_value}/draft"), permission="read", params=lambda draft: {'pid_value': draft.pid.pid_value}) publish = Link( template=URITemplate("/api/mocks/{pid_value}/draft/actions/publish"), permission="publish", params=lambda draft: {'pid_value': draft.pid.pid_value})
async def get_schema(subject_name, httpsession, host): headers = { 'Accept': 'application/vnd.schemaregistry.v1+json' } uri_temp = URITemplate(host + '/subjects{/subject}/versions{/version}') uri = uri_temp.expand(subject=subject_name, version='latest') r = await httpsession.get(uri, headers=headers) data = await r.json() schema = json.loads(data['schema']) schema_id = data['id'] return schema, schema_id
async def consume_for_simple_topics(*, loop, httpsession, consumer_settings, schema_registry_url, topic_name): consumer_settings.update({ 'group_id': topic_name, 'client_id': f'{topic_name}-0' # always only one consumer per topic }) logger = structlog.get_logger(__name__).bind( role='consumer', group=consumer_settings['group_id'], client_id=consumer_settings['client_id']) logger.info(f'Getting schemas for topic {topic_name}') registry_headers = {'Accept': 'application/vnd.schemaregistry.v1+json'} schema_uri = URITemplate(schema_registry_url + '/subjects{/subject}/versions/latest') # Get schemas r = await httpsession.get(schema_uri.expand(subject=f'{topic_name}-key'), headers=registry_headers) data = await r.json() r = await httpsession.get(schema_uri.expand(subject=f'{topic_name}-value'), headers=registry_headers) data = await r.json() value_schema = fastavro.parse_schema(json.loads(data['schema'])) # Start up the Kafka consumer consumer = aiokafka.AIOKafkaConsumer(loop=loop, **consumer_settings) # Main loop for consuming messages try: await consumer.start() # Subscribe to all topics in the experiment consumer.subscribe([topic_name]) logger.info(f'Started consumer for topic {topic_name}') while True: async for message in consumer: value_fh = BytesIO(message.value) value_fh.seek(0) value = fastavro.schemaless_reader(value_fh, value_schema) now = datetime.datetime.now(datetime.timezone.utc) latency = now - value['timestamp'] latency_millisec = \ latency.seconds * 1000 + latency.microseconds / 1000 CONSUMED.inc() # increment prometheus consumption counter LATENCY.observe(latency_millisec / 1000) LATENCY_SUMMARY.observe(latency_millisec / 1000) logger.debug('latency', latency_millisec=latency_millisec, topic=message.topic) finally: consumer.stop()
async def produce_for_simple_topic(*, loop, httpsession, producer_settings, schema_registry_url, topic_name, period): logger = structlog.get_logger(__name__).bind( role='producer', topic=topic_name, ) logger.info('Getting schemas') schema_uri = URITemplate(schema_registry_url + '/subjects{/subject}/versions/latest') headers = {'Accept': 'application/vnd.schemaregistry.v1+json'} # Get key schema r = await httpsession.get(schema_uri.expand(subject=topic_name + '-key'), headers=headers) data = await r.json() key_schema = fastavro.parse_schema(json.loads(data['schema'])) # Get value schema r = await httpsession.get(schema_uri.expand(subject=topic_name + '-value'), headers=headers) data = await r.json() value_schema = fastavro.parse_schema(json.loads(data['schema'])) default_key_fh = BytesIO() fastavro.schemaless_writer(default_key_fh, key_schema, {}) default_key_fh.seek(0) default_key = default_key_fh.read() # Set up producer producer = aiokafka.AIOKafkaProducer(loop=loop, **producer_settings) await producer.start() logger.info(f'Started producer') try: while True: message_fh = BytesIO() fastavro.schemaless_writer( message_fh, value_schema, {'timestamp': datetime.datetime.now(datetime.timezone.utc)}) message_fh.seek(0) # May want to adjust this control batching latency await producer.send_and_wait(topic_name, key=default_key, value=message_fh.read()) PRODUCED.inc() # increment prometheus production counter logger.debug('Sent message') # naieve message period; need to correct for production time await asyncio.sleep(period) finally: await producer.stop()
def buildKeyMapUrl(config_data): """ Formats the KVM Keymap url for us using the config data. """ t = URITemplate('https://{hostname}/{version}/organizations/{org}/environments/{env_name}/keyvaluemaps/{map_name}/entries/{entry_name}') return t.expand( hostname = config_data['hostname'], version = config_data['version'], org = config_data['org'], env_name = config_data['env_name'], map_name = config_data['map_name'], entry_name = config_data['entry_name'] )
class BibliographicUserRecordsSearchLinksSchemaV1(SearchLinksSchema): """User Record Links schema.""" self = Link(template=URITemplate("/api/user/records{?params*}"), permission="search", params=search_link_params(0)) prev = Link(template=URITemplate("/api/user/records{?params*}"), permission="search", params=search_link_params(-1), when=search_link_when(-1)) next = Link(template=URITemplate("/api/user/records{?params*}"), permission="search", params=search_link_params(+1), when=search_link_when(+1))
class BibliographicRecordLinksSchemaV1(RecordLinksSchema): """Record Links schema.""" # WARNING: It was intentionally decided that if # config.py::RECORDS_UI_ENDPOINTS is changed by the instance, # the instance also needs to overwrite this Schema (in the # links_config) self_html = Link(template=URITemplate("/records/{pid_value}"), permission="read", params=lambda record: {'pid_value': record.pid.pid_value}) files = Link(template=URITemplate("/api/records/{pid_value}/files"), permission="read", params=lambda record: {'pid_value': record.pid.pid_value})
def test_expand(self): """ This test ensures that expansion works as expected. """ # Single t = URITemplate("https://api.github.com/users{/user}") expanded = "https://api.github.com/users/sigmavirus24" self.assertEqual(t.expand(user="******"), expanded) v = t.variables[0] self.assertEqual(v.expand({"user": None}), {"/user": ""}) # Multiple t = URITemplate("https://api.github.com/users{/user}{/repo}") expanded = "https://api.github.com/users/sigmavirus24/github3.py" self.assertEqual(t.expand({"repo": "github3.py"}, user="******"), expanded)
def test_multi_path_matcher(self): template = URITemplate('{/path*}') url = '/one%2Ctwo%2Cthree/some/path' result = match(template, url) self.assertEqual({ 'path': ['one%2Ctwo%2Cthree', 'some', 'path'], }, result)
def _update_attributes(self, issue): self._api = issue["url"] self.assignee = issue["assignee"] if self.assignee: self.assignee = users.ShortUser(self.assignee, self) self.assignees = issue["assignees"] if self.assignees: self.assignees = [ users.ShortUser(assignee, self) for assignee in self.assignees ] self.body = issue["body"] self.closed_at = self._strptime(issue["closed_at"]) self.comments_count = issue["comments"] self.comments_url = issue["comments_url"] self.created_at = self._strptime(issue["created_at"]) self.events_url = issue["events_url"] self.html_url = issue["html_url"] self.id = issue["id"] self.labels_urlt = URITemplate(issue["labels_url"]) self.locked = issue["locked"] self.milestone = issue["milestone"] if self.milestone: self.milestone = milestone.Milestone(self.milestone, self) self.number = issue["number"] self.original_labels = issue["labels"] if self.original_labels: self.original_labels = [ label.ShortLabel(lbl, self) for lbl in self.original_labels ] self.pull_request_urls = issue.get("pull_request") self.state = issue["state"] self.title = issue["title"] self.updated_at = self._strptime(issue["updated_at"]) self.user = users.ShortUser(issue["user"], self)
class RecordLinksSchema(Schema): """Schema for a record's links.""" # NOTE: # - /api prefix is needed here because above are mounted on /api self_ = Link( template=URITemplate("/api/mocks/{pid_value}"), permission="read", params=lambda record: {'pid_value': record.pid.pid_value}, data_key="self" # To avoid using self since is python reserved key ) files = Link( template=URITemplate("/api/mocks/{pid_value}/files"), permission="read", params=lambda record: {'pid_value': record.pid.pid_value}, )
def test_single_path_matcher(self): template = URITemplate('{/count}') url = '/root/foo/bar' result = match(template, url) # nothing because additional path segment separator which does # not match. self.assertIsNone(result)
def reverse(self, lon=None, lat=None, types=None): """Returns a Requests response object that contains a GeoJSON collection of places near the given longitude and latitude. `response.geojson()` returns the geocoding result as GeoJSON. `response.status_code` returns the HTTP API status code. See: https://www.mapbox.com/api-documentation/#retrieve-places-near-a-location.""" uri = URITemplate(self.baseuri + '/{dataset}/{lon},{lat}.json').expand( dataset=self.name, lon=str(round(float(lon), self.precision.get('reverse', 5))), lat=str(round(float(lat), self.precision.get('reverse', 5)))) params = {} if types: params.update(self._validate_place_types(types)) resp = self.session.get(uri, params=params) self.handle_http_error(resp) # for consistency with other services def geojson(): return resp.json() resp.geojson = geojson return resp
def test_str_repr(self): uri = 'https://api.github.com{/endpoint}' t = URITemplate(uri) self.assertEqual(str(t), uri) self.assertEqual(str(t.variables[0]), '/endpoint') self.assertEqual(repr(t), 'URITemplate("%s")' % uri) self.assertEqual(repr(t.variables[0]), 'URIVariable(/endpoint)')
class Link: """Utility class for keeping track of and resolve links.""" def __init__(self, uritemplate, when=None, vars=None): """Constructor.""" self._uritemplate = URITemplate(uritemplate) self._when_func = when self._vars_func = vars def should_render(self, obj, ctx): """Determine if the link should be rendered.""" if self._when_func: return bool(self._when_func(obj, ctx)) return True @staticmethod def vars(obj, vars): """Subclasses can overwrite this method.""" pass def expand(self, obj, context): """Expand the URI Template.""" vars = {} vars.update(deepcopy(context)) self.vars(obj, vars) if self._vars_func: self._vars_func(obj, vars) vars = preprocess_vars(vars) return self._uritemplate.expand(**vars)
def test_expand(self): """ This test ensures that expansion works as expected. """ # Single t = URITemplate('https://api.github.com/users{/user}') expanded = 'https://api.github.com/users/sigmavirus24' self.assertEqual(t.expand(user='******'), expanded) v = t.variables[0] self.assertEqual(v.expand({'user': None}), {'/user': ''}) # Multiple t = URITemplate('https://api.github.com/users{/user}{/repo}') expanded = 'https://api.github.com/users/sigmavirus24/github3.py' self.assertEqual( t.expand({'repo': 'github3.py'}, user='******'), expanded )
def _update_attributes(self, user): self.avatar_url = user["avatar_url"] self.events_urlt = URITemplate(user["events_url"]) self.followers_url = user["followers_url"] self.following_urlt = URITemplate(user["following_url"]) self.gists_urlt = URITemplate(user["gists_url"]) self.gravatar_id = user["gravatar_id"] self.html_url = user["html_url"] self.id = user["id"] self.login = user["login"] self.organizations_url = user["organizations_url"] self.received_events_url = user["received_events_url"] self.repos_url = user["repos_url"] self.site_admin = user.get("site_admin") self.starred_urlt = URITemplate(user["starred_url"]) self.subscriptions_url = user["subscriptions_url"] self.type = user["type"] self.url = self._api = user["url"] self._uniq = self.id
def _update_attributes(self, release): self._api = self.url = release["url"] self.original_assets = [Asset(i, self) for i in release["assets"]] self.assets_url = release["assets_url"] self.author = users.ShortUser(release["author"], self) self.body = release["body"] self.created_at = self._strptime(release["created_at"]) self.draft = release["draft"] self.html_url = release["html_url"] self.id = release["id"] self.name = release["name"] self.prerelease = release["prerelease"] self.published_at = self._strptime(release["published_at"]) self.tag_name = release["tag_name"] self.tarball_url = release["tarball_url"] self.target_commitish = release["target_commitish"] self.upload_urlt = URITemplate(release["upload_url"]) self.zipball_url = release["zipball_url"]
class PriceURL: """ PriceURl implements an easily readible, funcational, and modifiable URL for retreiving prices :param item_type: :param item_number: :param =color_id: Usage: url_template = PriceURL() uri = url_template.expand(item_type=itemtypeID, item_number=itemID, color_id=itemColorID) 'https://www.bricklink.com/catalogPG.asp?P=3004&colorID=8' """ url = ('https://www.bricklink.com/catalogPG.asp?' '{item_type} = {item_number} &' 'colorID = {color_id}' ) def __init__(self): self.raw_url = PriceURL.url.replace(" ", "") # Spaces improved readability self.url_template = URITemplate(self.raw_url) def expand(self, itemtypeID, itemID, itemColorID): self.url = self.url_template.expand(item_type=itemtypeID, item_number=itemID, color_id=itemColorID) return self.url
class User(BaseAccount): """The :class:`User <User>` object. This handles and structures information in the `User section <http://developer.github.com/v3/users/>`_. Two user instances can be checked like so:: u1 == u2 u1 != u2 And is equivalent to:: u1.id == u2.id u1.id != u2.id """ def __init__(self, user, session=None): super(User, self).__init__(user, session) if not self.type: self.type = 'User' #: ID of the user's image on Gravatar self.gravatar_id = user.get('gravatar_id', '') #: True -- for hire, False -- not for hire self.hireable = user.get('hireable', False) ## The number of public_gists #: Number of public gists self.public_gists = user.get('public_gists', 0) # Private information #: How much disk consumed by the user self.disk_usage = user.get('disk_usage', 0) #: Number of private repos owned by this user self.owned_private_repos = user.get('owned_private_repos', 0) #: Number of private gists owned by this user self.total_private_gists = user.get('total_private_gists', 0) #: Total number of private repos self.total_private_repos = user.get('total_private_repos', 0) #: Which plan this user is on self.plan = Plan(user.get('plan', {})) events_url = user.get('events_url', '') #: Events URL Template. Expands with ``privacy`` self.events_urlt = URITemplate(events_url) if events_url else None #: Followers URL (not a template) self.followers_url = user.get('followers_url', '') furl = user.get('following_url', '') #: Following URL Template. Expands with ``other_user`` self.following_urlt = URITemplate(furl) if furl else None gists_url = user.get('gists_url', '') #: Gists URL Template. Expands with ``gist_id`` self.gists_urlt = URITemplate(gists_url) if gists_url else None #: Organizations URL (not a template) self.organizations_url = user.get('organizations_url', '') #: Received Events URL (not a template) self.received_events_url = user.get('received_events_url', '') #: Repostories URL (not a template) self.repos_url = user.get('repos_url', '') starred_url = user.get('starred_url', '') #: Starred URL Template. Expands with ``owner`` and ``repo`` self.starred_urlt = URITemplate(starred_url) if starred_url else None #: Subscriptions URL (not a template) self.subscriptions_url = user.get('subscriptions_url', '') def __str__(self): return self.login def _update_(self, user): self.__init__(user, self._session) @requires_auth def add_email_address(self, address): """Add the single email address to the authenticated user's account. :param str address: (required), email address to add :returns: list of email addresses """ return self.add_email_addresses([address]) @requires_auth def add_email_addresses(self, addresses=[]): """Add the email addresses in ``addresses`` to the authenticated user's account. :param list addresses: (optional), email addresses to be added :returns: list of email addresses """ json = [] if addresses: url = self._build_url('user', 'emails') json = self._json(self._post(url, data=addresses), 201) return json @requires_auth def delete_email_address(self, address): """Delete the email address from the user's account. :param str address: (required), email address to delete :returns: bool """ return self.delete_email_addresses([address]) @requires_auth def delete_email_addresses(self, addresses=[]): """Delete the email addresses in ``addresses`` from the authenticated user's account. :param list addresses: (optional), email addresses to be removed :returns: bool """ url = self._build_url('user', 'emails') return self._boolean(self._delete(url, data=dumps(addresses)), 204, 404) def is_assignee_on(self, login, repository): """Checks if this user can be assigned to issues on login/repository. :returns: :class:`bool` """ url = self._build_url('repos', login, repository, 'assignees', self.login) return self._boolean(self._get(url), 204, 404) def is_following(self, login): """Checks if this user is following ``login``. :param str login: (required) :returns: bool """ url = self.following_urlt.expand(other_user=login) return self._boolean(self._get(url), 204, 404) def iter_events(self, public=False, number=-1, etag=None): """Iterate over events performed by this user. :param bool public: (optional), only list public events for the authenticated user :param int number: (optional), number of events to return. Default: -1 returns all available events. :param str etag: (optional), ETag from a previous request to the same endpoint :returns: list of :class:`Event <github3.events.Event>`\ s """ path = ['events'] if public: path.append('public') url = self._build_url(*path, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def iter_followers(self, number=-1, etag=None): """Iterate over the followers of this user. :param int number: (optional), number of followers to return. Default: -1 returns all available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <User>`\ s """ url = self._build_url('followers', base_url=self._api) return self._iter(int(number), url, User, etag=etag) def iter_following(self, number=-1, etag=None): """Iterate over the users being followed by this user. :param int number: (optional), number of users to return. Default: -1 returns all available users :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <User>`\ s """ url = self._build_url('following', base_url=self._api) return self._iter(int(number), url, User, etag=etag) def iter_keys(self, number=-1, etag=None): """Iterate over the public keys of this user. .. versionadded:: 0.5 :param int number: (optional), number of keys to return. Default: -1 returns all available keys :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Key <Key>`\ s """ url = self._build_url('keys', base_url=self._api) return self._iter(int(number), url, Key, etag=etag) def iter_org_events(self, org, number=-1, etag=None): """Iterate over events as they appear on the user's organization dashboard. You must be authenticated to view this. :param str org: (required), name of the organization :param int number: (optional), number of events to return. Default: -1 returns all available events :param str etag: (optional), ETag from a previous request to the same endpoint :returns: list of :class:`Event <github3.events.Event>`\ s """ url = '' if org: url = self._build_url('events', 'orgs', org, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def iter_received_events(self, public=False, number=-1, etag=None): """Iterate over events that the user has received. If the user is the authenticated user, you will see private and public events, otherwise you will only see public events. :param bool public: (optional), determines if the authenticated user sees both private and public or just public :param int number: (optional), number of events to return. Default: -1 returns all events available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ path = ['received_events'] if public: path.append('public') url = self._build_url(*path, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def iter_starred(self, sort=None, direction=None, number=-1, etag=None): """Iterate over repositories starred by this user. .. versionchanged:: 0.5 Added sort and direction parameters (optional) as per the change in GitHub's API. :param int number: (optional), number of starred repos to return. Default: -1, returns all available repos :param str sort: (optional), either 'created' (when the star was created) or 'updated' (when the repository was last pushed to) :param str direction: (optional), either 'asc' or 'desc'. Default: 'desc' :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ from github3.repos import Repository params = {'sort': sort, 'direction': direction} self._remove_none(params) url = self.starred_urlt.expand(owner=None, repo=None) return self._iter(int(number), url, Repository, params, etag) def iter_subscriptions(self, number=-1, etag=None): """Iterate over repositories subscribed to by this user. :param int number: (optional), number of subscriptions to return. Default: -1, returns all available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ from github3.repos import Repository url = self._build_url('subscriptions', base_url=self._api) return self._iter(int(number), url, Repository, etag=etag) @requires_auth def update(self, name=None, email=None, blog=None, company=None, location=None, hireable=False, bio=None): """If authenticated as this user, update the information with the information provided in the parameters. :param str name: e.g., 'John Smith', not login name :param str email: e.g., '*****@*****.**' :param str blog: e.g., 'http://www.example.com/jsmith/blog' :param str company: :param str location: :param bool hireable: defaults to False :param str bio: GitHub flavored markdown :returns: bool """ user = {'name': name, 'email': email, 'blog': blog, 'company': company, 'location': location, 'hireable': hireable, 'bio': bio} self._remove_none(user) url = self._build_url('user') json = self._json(self._patch(url, data=dumps(user)), 200) if json: self._update_(json) return True return False
def __init__(self): self.raw_url = PriceURL.url.replace(" ", "") # Spaces improved readability self.url_template = URITemplate(self.raw_url)
class Release(GitHubCore): """The :class:`Release <Release>` object. It holds the information GitHub returns about a release from a :class:`Repository <github3.repos.repo.Repository>`. """ CUSTOM_HEADERS = {'Accept': 'application/vnd.github.manifold-preview'} def _update_attributes(self, release): self._api = release.get('url') #: List of :class:`Asset <Asset>` objects for this release self.original_assets = [ Asset(i, self) for i in release.get('assets', []) ] #: URL for uploaded assets self.assets_url = release.get('assets_url') #: Body of the release (the description) self.body = release.get('body') #: Date the release was created self.created_at = self._strptime(release.get('created_at')) #: Boolean whether value is True or False self.draft = release.get('draft') #: HTML URL of the release self.html_url = release.get('html_url') #: GitHub id self.id = release.get('id') #: Name given to the release self.name = release.get('name') #: Boolean whether release is a prerelease self.prerelease = release.get('prerelease') #: Date the release was published self.published_at = self._strptime(release.get('published_at')) #: Name of the tag self.tag_name = release.get('tag_name') #: URL to download a tarball of the release self.tarball_url = release.get('tarball_url') #: "Commit" that this release targets self.target_commitish = release.get('target_commitish') upload_url = release.get('upload_url') #: URITemplate to upload an asset with self.upload_urlt = URITemplate(upload_url) if upload_url else None #: URL to download a zipball of the release self.zipball_url = release.get('zipball_url') def _repr(self): return '<Release [{0}]>'.format(self.name) def archive(self, format, path=''): """Get the tarball or zipball archive for this release. :param str format: (required), accepted values: ('tarball', 'zipball') :param path: (optional), path where the file should be saved to, default is the filename provided in the headers and will be written in the current directory. it can take a file-like object as well :type path: str, file :returns: bool -- True if successful, False otherwise """ resp = None if format in ('tarball', 'zipball'): repo_url = self._api[:self._api.rfind('/releases')] url = self._build_url(format, self.tag_name, base_url=repo_url) resp = self._get(url, allow_redirects=True, stream=True) if resp and self._boolean(resp, 200, 404): utils.stream_response_to_file(resp, path) return True return False def asset(self, asset_id): """Retrieve the asset from this release with ``asset_id``. :param int asset_id: ID of the Asset to retrieve :returns: :class:`~github3.repos.release.Asset` """ json = None if int(asset_id) > 0: i = self._api.rfind('/') url = self._build_url('assets', str(asset_id), base_url=self._api[:i]) json = self._json(self._get(url), 200) return self._instance_or_null(Asset, json) def assets(self, number=-1, etag=None): """Iterate over the assets available for this release. :param int number: (optional), Number of assets to return :param str etag: (optional), last ETag header sent :returns: generator of :class:`Asset <Asset>` objects """ url = self._build_url('assets', base_url=self._api) return self._iter(number, url, Asset, etag=etag) @requires_auth def delete(self): """Users with push access to the repository can delete a release. :returns: True if successful; False if not successful """ url = self._api return self._boolean( self._delete(url, headers=Release.CUSTOM_HEADERS), 204, 404 ) @requires_auth def edit(self, tag_name=None, target_commitish=None, name=None, body=None, draft=None, prerelease=None): """Users with push access to the repository can edit a release. If the edit is successful, this object will update itself. :param str tag_name: (optional), Name of the tag to use :param str target_commitish: (optional), The "commitish" value that determines where the Git tag is created from. Defaults to the repository's default branch. :param str name: (optional), Name of the release :param str body: (optional), Description of the release :param boolean draft: (optional), True => Release is a draft :param boolean prerelease: (optional), True => Release is a prerelease :returns: True if successful; False if not successful """ url = self._api data = { 'tag_name': tag_name, 'target_commitish': target_commitish, 'name': name, 'body': body, 'draft': draft, 'prerelease': prerelease, } self._remove_none(data) r = self.session.patch( url, data=json.dumps(data), headers=Release.CUSTOM_HEADERS ) successful = self._boolean(r, 200, 404) if successful: # If the edit was successful, let's update the object. self._update_attributes(r.json()) return successful @requires_auth def upload_asset(self, content_type, name, asset, label=None): """Upload an asset to this release. All parameters are required. :param str content_type: The content type of the asset. Wikipedia has a list of common media types :param str name: The name of the file :param asset: The file or bytes object to upload. :param label: (optional), An alternate short description of the asset. :returns: :class:`Asset <Asset>` """ headers = {'Content-Type': content_type} params = {'name': name, 'label': label} self._remove_none(params) url = self.upload_urlt.expand(params) r = self._post(url, data=asset, json=False, headers=headers) if r.status_code in (201, 202): return Asset(r.json(), self) raise error_for(r)
def main(argv): try: parser = argparse.ArgumentParser(description="Arma Automatic Publishing Script") parser.add_argument('manifest', type=argparse.FileType('r'), help='manifest json file') parser.add_argument('-r', '--release_target', type=str, help="the name of the release target in the manifest file.", default="release") parser.add_argument('-v', '--version', type=str, help="the version of the release archive.", default="") args = parser.parse_args() manifest_file = args.manifest release_target = args.release_target version = args.version.split(".") manifest = json.load(manifest_file) if version == "": version = get_project_version("..\\addons\\\main\\script_version.hpp") if(not "CBA_PUBLISH_CREDENTIALS_PATH" in os.environ): raise Exception("CBA_PUBLISH_CREDENTIALS_PATH is not set in the environment") credentials_path = os.environ["CBA_PUBLISH_CREDENTIALS_PATH"] for destination in manifest['publish'][release_target]['destinations']: if(destination["type"] == "steam"): cred_file = json.load(open(os.path.join(credentials_path, destination["cred_file"]))) if("username" in cred_file and "password" in cred_file): steam_username = cred_file["username"] steam_password = cred_file["password"] start_steam_with_user(steam_username, steam_password) else: raise Exception("Credentials file did not specify a username and password for Steam login") if(not "project_id" in destination): raise Exception("Steam Publish","No project ID defined in manifest for Steam publish") project_id = destination["project_id"] if(not "release_dir" in destination): raise Exception("Steam Publish","No release directory defined in manifest for Steam publish") release_dir = destination["release_dir"] if(not "steam_changelog" in destination): raise Exception("Steam Publish","No steam changelog defined in manifest for Steam publish") steam_changelog = destination["steam_changelog"] steam_publish_folder(release_dir, project_id, version, steam_changelog) close_steam() if(destination["type"] == "sftp"): cred_file = json.load(open(os.path.join(credentials_path, destination["cred_file"]))) if("username" in cred_file and "password" in cred_file): sftp_username = cred_file["username"] sftp_password = cred_file["password"] else: raise Exception("Credentials file did not specify a username and password for SFTP login") if(not "hostname" in destination): raise Exception("SFTP Publish","No hostname was defined for the SFTP site.") hostname = destination["hostname"] if(not "local_path" in destination): raise Exception("SFTP Publish","No local path was defined for the SFTP upload.") local_path = destination["local_path"] if(not "remote_path" in destination): raise Exception("SFTP Publish","No remote path was defined for the SFTP upload.") remote_path = destination["remote_path"] cnopts = pysftp.CnOpts() cnopts.hostkeys = None sftp = pysftp.Connection(host=hostname, username=sftp_username, password=sftp_password, cnopts=cnopts) local_path = local_path.format(major=version[0], minor=version[1], patch=version[2], build=version[3]) remote_path = remote_path.format(major=version[0], minor=version[1], patch=version[2], build=version[3]) print("SFTP: Publishing {} to remote {}:{}".format(local_path, hostname, remote_path)) sftp.put(local_path, remotepath=remote_path) print("SFTP: Upload Complete!") if(destination["type"] == "github"): account = destination["account"] tag_name = destination["tag_name"] branch = destination["branch"] name = destination["name"] body_file = destination["body_file"] local_path = destination["local_path"] prerelease = destination["prerelease"] asset_name = destination["asset_name"] tag_name = tag_name.format(major=version[0], minor=version[1], patch=version[2], build=version[3]) name = name.format(major=version[0], minor=version[1], patch=version[2], build=version[3]) asset_name = asset_name.format(major=version[0], minor=version[1], patch=version[2], build=version[3]) local_path = local_path.format(major=version[0], minor=version[1], patch=version[2], build=version[3]) release_text_file = open(body_file, mode='r') release_text = release_text_file.read() release_text_file.close() create_request = { "tag_name": tag_name, "target_commitish": branch, "name": name, "body": release_text, "draft": False, "prerelease": prerelease } github_token = os.environ["IDI_GITHUB_TOKEN"] release_string = json.dumps(create_request, separators=(',',':')) temp_dir = tempfile.mkdtemp() tmpname = os.path.join(temp_dir,"jsonpost") temp_file = open(tmpname, 'w') temp_file.write(release_string) temp_file.close() curl_string = ' '.join(["curl", '-s', '-H "Authorization: token {}"'.format(github_token), '-H "Content-Type: application/json"', "--request POST", "--data", '"@{}"'.format(tmpname).replace('\\','\\\\'), "https://api.github.com/repos/{}/releases".format(account)]) print("Creating Github Release...") response = subprocess.check_output(curl_string) response_json = json.loads(response.decode("ascii")) shutil.rmtree(temp_dir) if("id" in response_json): print("Github Release Created @ {}".format(response_json["url"])) release_id = response_json["id"] upload_url = response_json["upload_url"] t = URITemplate(upload_url) upload_url = t.expand(name=asset_name) curl_string = ' '.join(["curl", '-s', '-H "Authorization: token {}"'.format(github_token), '-H "Content-Type: application/zip"', "--data-binary", '"@{}"'.format(local_path), upload_url]) print("Attaching Asset...") response = subprocess.check_output(curl_string) response_json = json.loads(response.decode("ascii")) if("browser_download_url" in response_json): print("Asset Attached @ {}".format(response_json["browser_download_url"])) else: print(response_json) raise Exception("Github Publish","Failed to Attach Asset") else: print(response_json) raise Exception("Github Publish","Failed to Create Release") except Exception as e: print(e) sys.exit(1)
class _User(models.GitHubCore): """The :class:`User <User>` object. This handles and structures information in the `User section`_. Two user instances can be checked like so:: u1 == u2 u1 != u2 And is equivalent to:: u1.id == u2.id u1.id != u2.id .. _User section: http://developer.github.com/v3/users/ """ class_name = "_User" def _update_attributes(self, user): self.avatar_url = user["avatar_url"] self.events_urlt = URITemplate(user["events_url"]) self.followers_url = user["followers_url"] self.following_urlt = URITemplate(user["following_url"]) self.gists_urlt = URITemplate(user["gists_url"]) self.gravatar_id = user["gravatar_id"] self.html_url = user["html_url"] self.id = user["id"] self.login = user["login"] self.organizations_url = user["organizations_url"] self.received_events_url = user["received_events_url"] self.repos_url = user["repos_url"] self.site_admin = user.get("site_admin") self.starred_urlt = URITemplate(user["starred_url"]) self.subscriptions_url = user["subscriptions_url"] self.type = user["type"] self.url = self._api = user["url"] self._uniq = self.id def __str__(self): return self.login def _repr(self): full_name = "" name = getattr(self, "name", None) if name is not None: full_name = ":{}".format(name) return "<{s.class_name} [{s.login}{full_name}]>".format( s=self, full_name=full_name ) def is_assignee_on(self, username, repository): """Check if this user can be assigned to issues on username/repository. :param str username: owner's username of the repository :param str repository: name of the repository :returns: True if the use can be assigned, False otherwise :rtype: :class:`bool` """ url = self._build_url( "repos", username, repository, "assignees", self.login ) return self._boolean(self._get(url), 204, 404) def is_following(self, username): """Check if this user is following ``username``. :param str username: (required) :returns: bool """ url = self.following_urlt.expand(other_user=username) return self._boolean(self._get(url), 204, 404) def events(self, public=False, number=-1, etag=None): r"""Iterate over events performed by this user. :param bool public: (optional), only list public events for the authenticated user :param int number: (optional), number of events to return. Default: -1 returns all available events. :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ path = ["events"] if public: path.append("public") url = self._build_url(*path, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def followers(self, number=-1, etag=None): r"""Iterate over the followers of this user. :param int number: (optional), number of followers to return. Default: -1 returns all available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <User>`\ s """ url = self._build_url("followers", base_url=self._api) return self._iter(int(number), url, ShortUser, etag=etag) def following(self, number=-1, etag=None): r"""Iterate over the users being followed by this user. :param int number: (optional), number of users to return. Default: -1 returns all available users :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <User>`\ s """ url = self._build_url("following", base_url=self._api) return self._iter(int(number), url, ShortUser, etag=etag) def gpg_keys(self, number=-1, etag=None): r"""Iterate over the GPG keys of this user. .. versionadded:: 1.2.0 :param int number: (optional), number of GPG keys to return. Default: -1 returns all available GPG keys :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`GPGKey <GPGKey>`\ s """ url = self._build_url("gpg_keys", base_url=self._api) return self._iter(int(number), url, GPGKey, etag=etag) def keys(self, number=-1, etag=None): r"""Iterate over the public keys of this user. .. versionadded:: 0.5 :param int number: (optional), number of keys to return. Default: -1 returns all available keys :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Key <Key>`\ s """ url = self._build_url("keys", base_url=self._api) return self._iter(int(number), url, Key, etag=etag) @requires_auth def organization_events(self, org, number=-1, etag=None): r"""Iterate over events from the user's organization dashboard. .. note:: You must be authenticated to view this. :param str org: (required), name of the organization :param int number: (optional), number of events to return. Default: -1 returns all available events :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ url = "" if org: url = self._build_url("events", "orgs", org, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def received_events(self, public=False, number=-1, etag=None): r"""Iterate over events that the user has received. If the user is the authenticated user, you will see private and public events, otherwise you will only see public events. :param bool public: (optional), determines if the authenticated user sees both private and public or just public :param int number: (optional), number of events to return. Default: -1 returns all events available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ path = ["received_events"] if public: path.append("public") url = self._build_url(*path, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def organizations(self, number=-1, etag=None): r"""Iterate over organizations the user is member of. :param int number: (optional), number of organizations to return. Default: -1 returns all available organization :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`ShortOrganization <github3.orgs.ShortOrganization>`\ s """ # Import here, because a toplevel import causes an import loop from .orgs import ShortOrganization url = self._build_url("orgs", base_url=self._api) return self._iter(int(number), url, ShortOrganization, etag=etag) def starred_repositories( self, sort=None, direction=None, number=-1, etag=None ): """Iterate over repositories starred by this user. .. versionchanged:: 0.5 Added sort and direction parameters (optional) as per the change in GitHub's API. :param int number: (optional), number of starred repos to return. Default: -1, returns all available repos :param str sort: (optional), either 'created' (when the star was created) or 'updated' (when the repository was last pushed to) :param str direction: (optional), either 'asc' or 'desc'. Default: 'desc' :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`~github3.repos.repo.StarredRepository` """ from .repos import Repository, StarredRepository params = {"sort": sort, "direction": direction} self._remove_none(params) url = self.starred_urlt.expand(owner=None, repo=None) return self._iter( int(number), url, StarredRepository, params, etag, headers=Repository.STAR_HEADERS, ) def subscriptions(self, number=-1, etag=None): """Iterate over repositories subscribed to by this user. :param int number: (optional), number of subscriptions to return. Default: -1, returns all available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ from .repos import ShortRepository url = self._build_url("subscriptions", base_url=self._api) return self._iter(int(number), url, ShortRepository, etag=etag) @requires_auth def rename(self, login): """Rename the user. .. note:: This is only available for administrators of a GitHub Enterprise instance. :param str login: (required), new name of the user :returns: bool """ url = self._build_url("admin", "users", self.login) payload = {"login": login} resp = self._boolean(self._patch(url, data=payload), 202, 403) return resp @requires_auth def impersonate(self, scopes=None): """Obtain an impersonation token for the user. The retrieved token will allow impersonation of the user. This is only available for admins of a GitHub Enterprise instance. :param list scopes: (optional), areas you want this token to apply to, i.e., 'gist', 'user' :returns: :class:`Authorization <Authorization>` """ url = self._build_url("admin", "users", self.login, "authorizations") data = {} if scopes: data["scopes"] = scopes json = self._json(self._post(url, data=data), 201) return self._instance_or_null(Authorization, json) @requires_auth def revoke_impersonation(self): """Revoke all impersonation tokens for the current user. This is only available for admins of a GitHub Enterprise instance. :returns: bool -- True if successful, False otherwise """ url = self._build_url("admin", "users", self.login, "authorizations") return self._boolean(self._delete(url), 204, 403) @requires_auth def promote(self): """Promote a user to site administrator. This is only available for admins of a GitHub Enterprise instance. :returns: bool -- True if successful, False otherwise """ url = self._build_url("site_admin", base_url=self._api) return self._boolean(self._put(url), 204, 403) @requires_auth def demote(self): """Demote a site administrator to simple user. You can demote any user account except your own. This is only available for admins of a GitHub Enterprise instance. :returns: bool -- True if successful, False otherwise """ url = self._build_url("site_admin", base_url=self._api) return self._boolean(self._delete(url), 204, 403) @requires_auth def suspend(self): """Suspend the user. This is only available for admins of a GitHub Enterprise instance. This API is disabled if you use LDAP, check the GitHub API dos for more information. :returns: bool -- True if successful, False otherwise """ url = self._build_url("suspended", base_url=self._api) return self._boolean(self._put(url), 204, 403) @requires_auth def unsuspend(self): """Unsuspend the user. This is only available for admins of a GitHub Enterprise instance. This API is disabled if you use LDAP, check the GitHub API dos for more information. :returns: bool -- True if successful, False otherwise """ url = self._build_url("suspended", base_url=self._api) return self._boolean(self._delete(url), 204, 403) @requires_auth def delete(self): """Delete the user. Per GitHub API documentation, it is often preferable to suspend the user. .. note:: This is only available for admins of a GitHub Enterprise instance. :returns: bool -- True if successful, False otherwise """ url = self._build_url("admin", "users", self.login) return self._boolean(self._delete(url), 204, 403)
class Release(models.GitHubCore): """Representation of a GitHub release. It holds the information GitHub returns about a release from a :class:`Repository <github3.repos.repo.Repository>`. Please see GitHub's `Releases Documentation`_ for more information. This object has the following attributes: .. attribute:: original_assets A list of :class:`~github3.repos.release.Asset` objects representing the assets uploaded for this relesae. .. attribute:: assets_url The URL to retrieve the assets from the API. .. attribute:: author A :class:`~github3.users.ShortUser` representing the creator of this release. .. attribute:: body The description of this release as written by the release creator. .. attribute:: created_at A :class:`~datetime.datetime` object representing the date and time when this release was created. .. attribute:: draft A boolean attribute describing whether this release is a draft. .. attribute:: html_url The URL to view this release in a browser. .. attribute:: id The unique identifier of this release. .. attribute:: name The name given to this release by the :attr:`author`. .. attribute:: prerelease A boolean attribute indicating whether the release is a pre-release. .. attribute:: published_at A :class:`~datetime.datetime` object representing the date and time when this release was publisehd. .. attribute:: tag_name The name of the tag associated with this release. .. attribute:: tarball_url The URL to retrieve a GitHub generated tarball for this release from the API. .. attribute:: target_commitish The reference (usually a commit) that is targetted by this release. .. attribute:: upload_urlt A :class:`~uritemplate.URITemplate` object that expands to form the URL to upload assets to. .. attribute:: zipball_url The URL to retrieve a GitHub generated zipball for this release from the API. .. _Releases Documentation: https://developer.github.com/v3/repos/releases/ """ def _update_attributes(self, release): self._api = self.url = release["url"] self.original_assets = [Asset(i, self) for i in release["assets"]] self.assets_url = release["assets_url"] self.author = users.ShortUser(release["author"], self) self.body = release["body"] self.created_at = self._strptime(release["created_at"]) self.draft = release["draft"] self.html_url = release["html_url"] self.id = release["id"] self.name = release["name"] self.prerelease = release["prerelease"] self.published_at = self._strptime(release["published_at"]) self.tag_name = release["tag_name"] self.tarball_url = release["tarball_url"] self.target_commitish = release["target_commitish"] self.upload_urlt = URITemplate(release["upload_url"]) self.zipball_url = release["zipball_url"] def _repr(self): return "<Release [{0}]>".format(self.name) def archive(self, format, path=""): """Get the tarball or zipball archive for this release. :param str format: (required), accepted values: ('tarball', 'zipball') :param path: (optional), path where the file should be saved to, default is the filename provided in the headers and will be written in the current directory. It can take a file-like object as well :type path: str, file :returns: True if successful, False otherwise :rtype: bool """ resp = None if format in ("tarball", "zipball"): repo_url = self._api[: self._api.rfind("/releases")] url = self._build_url(format, self.tag_name, base_url=repo_url) resp = self._get(url, allow_redirects=True, stream=True) if resp and self._boolean(resp, 200, 404): utils.stream_response_to_file(resp, path) return True return False def asset(self, asset_id): """Retrieve the asset from this release with ``asset_id``. :param int asset_id: ID of the Asset to retrieve :returns: the specified asset, if it exists :rtype: :class:`~github3.repos.release.Asset` """ json = None if int(asset_id) > 0: i = self._api.rfind("/") url = self._build_url( "assets", str(asset_id), base_url=self._api[:i] ) json = self._json(self._get(url), 200) return self._instance_or_null(Asset, json) def assets(self, number=-1, etag=None): """Iterate over the assets available for this release. :param int number: (optional), Number of assets to return :param str etag: (optional), last ETag header sent :returns: generator of asset objects :rtype: :class:`~github3.repos.release.Asset` """ url = self._build_url("assets", base_url=self._api) return self._iter(number, url, Asset, etag=etag) @requires_auth def delete(self): """Delete this release. Only users with push access to the repository can delete a release. :returns: True if successful; False if not successful :rtype: bool """ url = self._api return self._boolean(self._delete(url), 204, 404) @requires_auth def edit( self, tag_name=None, target_commitish=None, name=None, body=None, draft=None, prerelease=None, ): """Edit this release. Only users with push access to the repository can edit a release. If the edit is successful, this object will update itself. :param str tag_name: (optional), Name of the tag to use :param str target_commitish: (optional), The "commitish" value that determines where the Git tag is created from. Defaults to the repository's default branch. :param str name: (optional), Name of the release :param str body: (optional), Description of the release :param boolean draft: (optional), True => Release is a draft :param boolean prerelease: (optional), True => Release is a prerelease :returns: True if successful; False if not successful :rtype: bool """ url = self._api data = { "tag_name": tag_name, "target_commitish": target_commitish, "name": name, "body": body, "draft": draft, "prerelease": prerelease, } self._remove_none(data) r = self.session.patch(url, data=json.dumps(data)) successful = self._boolean(r, 200, 404) if successful: # If the edit was successful, let's update the object. self._update_attributes(r.json()) return successful @requires_auth def upload_asset(self, content_type, name, asset, label=None): """Upload an asset to this release. .. note:: All parameters are required. :param str content_type: The content type of the asset. Wikipedia has a list of common media types :param str name: The name of the file :param asset: The file or bytes object to upload. :param label: (optional), An alternate short description of the asset. :returns: the created asset :rtype: :class:`~github3.repos.release.Asset` """ headers = {"Content-Type": content_type} params = {"name": name, "label": label} self._remove_none(params) url = self.upload_urlt.expand(params) r = self._post(url, data=asset, json=False, headers=headers) if r.status_code in (201, 202): return Asset(r.json(), self) raise error_for(r)
def _update_attributes(self, user): super(User, self)._update_attributes(user) if not self.type: self.type = 'User' #: ID of the user's image on Gravatar self.gravatar_id = user.get('gravatar_id', '') #: True -- for hire, False -- not for hire self.hireable = user.get('hireable', False) # The number of public_gists #: Number of public gists self.public_gists = user.get('public_gists', 0) # Private information #: How much disk consumed by the user self.disk_usage = user.get('disk_usage', 0) #: Number of private repos owned by this user self.owned_private_repos = user.get('owned_private_repos', 0) #: Number of private gists owned by this user self.total_private_gists = user.get('total_private_gists', 0) #: Total number of private repos self.total_private_repos = user.get('total_private_repos', 0) #: Which plan this user is on self.plan = Plan(user.get('plan', {})) events_url = user.get('events_url', '') #: Events URL Template. Expands with ``privacy`` self.events_urlt = URITemplate(events_url) if events_url else None #: Followers URL (not a template) self.followers_url = user.get('followers_url', '') furl = user.get('following_url', '') #: Following URL Template. Expands with ``other_user`` self.following_urlt = URITemplate(furl) if furl else None gists_url = user.get('gists_url', '') #: Gists URL Template. Expands with ``gist_id`` self.gists_urlt = URITemplate(gists_url) if gists_url else None #: Organizations URL (not a template) self.organizations_url = user.get('organizations_url', '') #: Received Events URL (not a template) self.received_events_url = user.get('received_events_url', '') #: Repostories URL (not a template) self.repos_url = user.get('repos_url', '') starred_url = user.get('starred_url', '') #: Starred URL Template. Expands with ``owner`` and ``repo`` self.starred_urlt = URITemplate(starred_url) if starred_url else None #: Subscriptions URL (not a template) self.subscriptions_url = user.get('subscriptions_url', '') #: Number of repo contributions. Only appears in ``repo.contributors`` contributions = user.get('contributions') # The refresh method uses __init__ to replace the attributes on the # instance with what it receives from the /users/:username endpoint. # What that means is that contributions is no longer returned and as # such is changed because it doesn't exist. This guards against that. if contributions is not None: self.contributions = contributions self._uniq = user.get('id', None)
def test_no_mutate(self): args = {} t = URITemplate('') t.expand(args, key=1) self.assertEqual(args, {})
class User(BaseAccount): """The :class:`User <User>` object. This handles and structures information in the `User section <http://developer.github.com/v3/users/>`_. Two user instances can be checked like so:: u1 == u2 u1 != u2 And is equivalent to:: u1.id == u2.id u1.id != u2.id """ def _update_attributes(self, user): super(User, self)._update_attributes(user) if not self.type: self.type = 'User' #: ID of the user's image on Gravatar self.gravatar_id = user.get('gravatar_id', '') #: True -- for hire, False -- not for hire self.hireable = user.get('hireable', False) # The number of public_gists #: Number of public gists self.public_gists = user.get('public_gists', 0) # Private information #: How much disk consumed by the user self.disk_usage = user.get('disk_usage', 0) #: Number of private repos owned by this user self.owned_private_repos = user.get('owned_private_repos', 0) #: Number of private gists owned by this user self.total_private_gists = user.get('total_private_gists', 0) #: Total number of private repos self.total_private_repos = user.get('total_private_repos', 0) #: Which plan this user is on self.plan = Plan(user.get('plan', {})) events_url = user.get('events_url', '') #: Events URL Template. Expands with ``privacy`` self.events_urlt = URITemplate(events_url) if events_url else None #: Followers URL (not a template) self.followers_url = user.get('followers_url', '') furl = user.get('following_url', '') #: Following URL Template. Expands with ``other_user`` self.following_urlt = URITemplate(furl) if furl else None gists_url = user.get('gists_url', '') #: Gists URL Template. Expands with ``gist_id`` self.gists_urlt = URITemplate(gists_url) if gists_url else None #: Organizations URL (not a template) self.organizations_url = user.get('organizations_url', '') #: Received Events URL (not a template) self.received_events_url = user.get('received_events_url', '') #: Repostories URL (not a template) self.repos_url = user.get('repos_url', '') starred_url = user.get('starred_url', '') #: Starred URL Template. Expands with ``owner`` and ``repo`` self.starred_urlt = URITemplate(starred_url) if starred_url else None #: Subscriptions URL (not a template) self.subscriptions_url = user.get('subscriptions_url', '') #: Number of repo contributions. Only appears in ``repo.contributors`` contributions = user.get('contributions') # The refresh method uses __init__ to replace the attributes on the # instance with what it receives from the /users/:username endpoint. # What that means is that contributions is no longer returned and as # such is changed because it doesn't exist. This guards against that. if contributions is not None: self.contributions = contributions self._uniq = user.get('id', None) def __str__(self): return self.login @requires_auth def add_email_address(self, address): """Add the single email address to the authenticated user's account. :param str address: (required), email address to add :returns: list of email addresses """ return self.add_email_addresses([address]) @requires_auth def add_email_addresses(self, addresses=[]): """Add the email addresses in ``addresses`` to the authenticated user's account. :param list addresses: (optional), email addresses to be added :returns: list of email addresses """ json = [] if addresses: url = self._build_url('user', 'emails') json = self._json(self._post(url, data=addresses), 201) return json @requires_auth def delete_email_address(self, address): """Delete the email address from the user's account. :param str address: (required), email address to delete :returns: bool """ return self.delete_email_addresses([address]) @requires_auth def delete_email_addresses(self, addresses=[]): """Delete the email addresses in ``addresses`` from the authenticated user's account. :param list addresses: (optional), email addresses to be removed :returns: bool """ url = self._build_url('user', 'emails') return self._boolean(self._delete(url, data=dumps(addresses)), 204, 404) def is_assignee_on(self, username, repository): """Check if this user can be assigned to issues on username/repository. :param str username: owner's username of the repository :param str repository: name of the repository :returns: True if the use can be assigned, False otherwise :rtype: :class:`bool` """ url = self._build_url('repos', username, repository, 'assignees', self.login) return self._boolean(self._get(url), 204, 404) def is_following(self, username): """Checks if this user is following ``username``. :param str username: (required) :returns: bool """ url = self.following_urlt.expand(other_user=username) return self._boolean(self._get(url), 204, 404) def events(self, public=False, number=-1, etag=None): """Iterate over events performed by this user. :param bool public: (optional), only list public events for the authenticated user :param int number: (optional), number of events to return. Default: -1 returns all available events. :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ path = ['events'] if public: path.append('public') url = self._build_url(*path, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def followers(self, number=-1, etag=None): """Iterate over the followers of this user. :param int number: (optional), number of followers to return. Default: -1 returns all available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <User>`\ s """ url = self._build_url('followers', base_url=self._api) return self._iter(int(number), url, User, etag=etag) def following(self, number=-1, etag=None): """Iterate over the users being followed by this user. :param int number: (optional), number of users to return. Default: -1 returns all available users :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <User>`\ s """ url = self._build_url('following', base_url=self._api) return self._iter(int(number), url, User, etag=etag) def keys(self, number=-1, etag=None): """Iterate over the public keys of this user. .. versionadded:: 0.5 :param int number: (optional), number of keys to return. Default: -1 returns all available keys :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Key <Key>`\ s """ url = self._build_url('keys', base_url=self._api) return self._iter(int(number), url, Key, etag=etag) @requires_auth def organization_events(self, org, number=-1, etag=None): """Iterate over events as they appear on the user's organization dashboard. You must be authenticated to view this. :param str org: (required), name of the organization :param int number: (optional), number of events to return. Default: -1 returns all available events :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ url = '' if org: url = self._build_url('events', 'orgs', org, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def received_events(self, public=False, number=-1, etag=None): """Iterate over events that the user has received. If the user is the authenticated user, you will see private and public events, otherwise you will only see public events. :param bool public: (optional), determines if the authenticated user sees both private and public or just public :param int number: (optional), number of events to return. Default: -1 returns all events available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.events.Event>`\ s """ path = ['received_events'] if public: path.append('public') url = self._build_url(*path, base_url=self._api) return self._iter(int(number), url, Event, etag=etag) def organizations(self, number=-1, etag=None): """Iterate over organizations the user is member of :param int number: (optional), number of organizations to return. Default: -1 returns all available organization :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Event <github3.orgs.Organization>`\ s """ # Import here, because a toplevel import causes an import loop from .orgs import Organization url = self._build_url('orgs', base_url=self._api) return self._iter(int(number), url, Organization, etag=etag) def starred_repositories(self, sort=None, direction=None, number=-1, etag=None): """Iterate over repositories starred by this user. .. versionchanged:: 0.5 Added sort and direction parameters (optional) as per the change in GitHub's API. :param int number: (optional), number of starred repos to return. Default: -1, returns all available repos :param str sort: (optional), either 'created' (when the star was created) or 'updated' (when the repository was last pushed to) :param str direction: (optional), either 'asc' or 'desc'. Default: 'desc' :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ from .repos import Repository params = {'sort': sort, 'direction': direction} self._remove_none(params) url = self.starred_urlt.expand(owner=None, repo=None) return self._iter(int(number), url, Repository, params, etag) def subscriptions(self, number=-1, etag=None): """Iterate over repositories subscribed to by this user. :param int number: (optional), number of subscriptions to return. Default: -1, returns all available :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ from .repos import Repository url = self._build_url('subscriptions', base_url=self._api) return self._iter(int(number), url, Repository, etag=etag)
class Release(GitHubCore): """The :class:`Release <Release>` object. It holds the information GitHub returns about a release from a :class:`Repository <github3.repos.repo.Repository>`. """ CUSTOM_HEADERS = {'Accept': 'application/vnd.github.manifold-preview'} def __init__(self, release, session=None): super(Release, self).__init__(release, session) self._api = release.get('url') #: List of :class:`Asset <Asset>` objects for this release self.assets = [Asset(i, self) for i in release.get('assets', [])] #: URL for uploaded assets self.assets_url = release.get('assets_url') #: Body of the release (the description) self.body = release.get('body') #: Date the release was created self.created_at = self._strptime(release.get('created_at')) #: Boolean whether value is True or False self.draft = release.get('draft') #: HTML URL of the release self.html_url = release.get('html_url') #: GitHub id self.id = release.get('id') #: Name given to the release self.name = release.get('name') #; Boolean whether release is a prerelease self.prerelease = release.get('prerelease') #: Date the release was published self.published_at = self._strptime(release.get('published_at')) #: Name of the tag self.tag_name = release.get('tag_name') #: "Commit" that this release targets self.target_commitish = release.get('target_commitish') upload_url = release.get('upload_url') #: URITemplate to upload an asset with self.upload_urlt = URITemplate(upload_url) if upload_url else None def _repr(self): return '<Release [{0}]>'.format(self.name) @requires_auth def delete(self): """Users with push access to the repository can delete a release. :returns: True if successful; False if not successful """ url = self._api return self._boolean( self._delete(url, headers=Release.CUSTOM_HEADERS), 204, 404 ) @requires_auth def edit(self, tag_name=None, target_commitish=None, name=None, body=None, draft=None, prerelease=None): """Users with push access to the repository can edit a release. If the edit is successful, this object will update itself. :param str tag_name: (optional), Name of the tag to use :param str target_commitish: (optional), The "commitish" value that determines where the Git tag is created from. Defaults to the repository's default branch. :param str name: (optional), Name of the release :param str body: (optional), Description of the release :param boolean draft: (optional), True => Release is a draft :param boolean prerelease: (optional), True => Release is a prerelease :returns: True if successful; False if not successful """ url = self._api data = { 'tag_name': tag_name, 'target_commitish': target_commitish, 'name': name, 'body': body, 'draft': draft, 'prerelease': prerelease, } self._remove_none(data) r = self._session.patch( url, data=json.dumps(data), headers=Release.CUSTOM_HEADERS ) successful = self._boolean(r, 200, 404) if successful: # If the edit was successful, let's update the object. self.__init__(r.json(), self) return successful def iter_assets(self, number=-1, etag=None): """Iterate over the assets available for this release. :param int number: (optional), Number of assets to return :param str etag: (optional), last ETag header sent :returns: generator of :class:`Asset <Asset>` objects """ url = self._build_url('assets', base_url=self._api) return self._iter(number, url, Asset, etag=etag) @requires_auth def upload_asset(self, content_type, name, asset): """Upload an asset to this release. All parameters are required. :param str content_type: The content type of the asset. Wikipedia has a list of common media types :param str name: The name of the file :param asset: The file or bytes object to upload. :returns: :class:`Asset <Asset>` """ headers = Release.CUSTOM_HEADERS.copy() headers.update({'Content-Type': content_type}) url = self.upload_urlt.expand({'name': name}) r = self._post(url, data=asset, json=False, headers=headers, verify=False) if r.status_code in (201, 202): return Asset(r.json(), self) raise GitHubError(r)
def _test_(self): for k, v in d.items(): t = URITemplate(k) self.assertEqual(t.expand(v['expansion']), v['expected'])
def __get_url(self, path, id): rootUrl = self.hal.get_root_url() template = URITemplate(rootUrl + path); return template.expand(id= str(id) if id is not None else "")
def test_default_value(self): uri = "https://api.github.com/user{/user=sigmavirus24}" t = URITemplate(uri) self.assertEqual(t.expand(), "https://api.github.com/user/sigmavirus24") self.assertEqual(t.expand(user="******"), "https://api.github.com/user/lukasa")