Explore all Privacy open source software, libraries, packages, source code, cloud functions and APIs.

Popular New Releases in Privacy

pi-hole

v5.10

uBlock

1.42.5b1

server

ArchiveBox

v0.6.2: >10x performance gain, new Admin UI & CLI features, and more

Bilibili-Evolved

v2.1.7

Popular Libraries in Privacy

awesome-selfhosted

by awesome-selfhosted doticonjavascriptdoticon

star image 84418 doticonNOASSERTION

A list of Free Software network services and web applications which can be hosted on your own servers

pi-hole

by pi-hole doticonshelldoticon

star image 35862 doticonNOASSERTION

A black hole for Internet advertisements

uBlock

by gorhill doticonjavascriptdoticon

star image 29921 doticonGPL-3.0

uBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean.

server

by nextcloud doticonphpdoticon

star image 18911 doticonNOASSERTION

☁️ Nextcloud server, a safe home for all your data

macOS-Security-and-Privacy-Guide

by drduh doticonpythondoticon

star image 18109 doticonMIT

Guide to securing and improving privacy on macOS

ArchiveBox

by ArchiveBox doticonpythondoticon

star image 13237 doticonMIT

🗃 Open source self-hosted web archiving. Takes URLs/browser history/bookmarks/Pocket/Pinboard/etc., saves HTML, JS, PDFs, media, and more...

Bilibili-Evolved

by the1812 doticontypescriptdoticon

star image 12269 doticonMIT

强大的哔哩哔哩增强脚本

sovereign

by sovereign doticonhtmldoticon

star image 10012 doticonNOASSERTION

A set of Ansible playbooks to build and maintain your own private cloud: email, calendar, contacts, file sync, IRC bouncer, VPN, and more.

uBlock

by uBlock-LLC doticonjavascriptdoticon

star image 8025 doticonGPL-3.0

uBlock: a fast, lightweight, and lean blocker for Chrome, Firefox, and Safari.

Trending New libraries in Privacy

fawkes

by Shawn-Shan doticonpythondoticon

star image 4251 doticonBSD-3-Clause

Fawkes, privacy preserving tool against facial recognition systems. More info at https://sandlab.cs.uchicago.edu/fawkes

flame

by pawelmalak doticontypescriptdoticon

star image 2108 doticonMIT

Flame is self-hosted startpage for your server. Easily manage your apps and bookmarks with built-in editors.

maza-ad-blocking

by tanrax doticonshelldoticon

star image 1511 doticonApache-2.0

Local ad blocker. Like Pi-hole but local and using your operating system.

privatezilla

by builtbybel doticoncsharpdoticon

star image 1450 doticonMIT

👀👮🐢🔥Performs a privacy & security check of Windows 10

gravity-sync

by vmstan doticonshelldoticon

star image 1052 doticonGPL-3.0

💫 The easy way to synchronize the DNS configuration of two Pi-hole 5.x instances.

ttv-ublock

by odensc doticonjavascriptdoticon

star image 985 doticonMIT

Blocking ads on that certain streaming website

spotify-adblock

by abba23 doticonrustdoticon

star image 577 doticon

Adblocker for Spotify

Adblock4limbo

by limbopro doticoncssdoticon

star image 540 doticon

毒奶去广告计划(稳定版)For Quantumult X ;如去奈菲影视/低端影视/片库网/Pornhub/Jable/Netflav/HPjav等视频网站广告或其他ACG网站广告;

heimdall

by fterh doticontypescriptdoticon

star image 462 doticonMIT

Self-hosted personal email guardian with one-step deployment

Top Authors in Privacy

1

YunoHost-Apps

34 Libraries

star icon429

2

AdguardTeam

18 Libraries

star icon6208

3

EnergizedProtection

7 Libraries

star icon1697

4

mozilla

6 Libraries

star icon127

5

nextcloud

5 Libraries

star icon19908

6

adblockplus

4 Libraries

star icon94

7

eyeo/adblockplus

4 Libraries

star icon23

8

boreq

3 Libraries

star icon43

9

LV-Crew

3 Libraries

star icon25

10

gorhill

3 Libraries

star icon30651

1

34 Libraries

star icon429

2

18 Libraries

star icon6208

3

7 Libraries

star icon1697

4

6 Libraries

star icon127

5

5 Libraries

star icon19908

6

4 Libraries

star icon94

7

4 Libraries

star icon23

8

3 Libraries

star icon43

9

3 Libraries

star icon25

10

3 Libraries

star icon30651

Trending Kits in Privacy

No Trending Kits are available at this moment for Privacy

Trending Discussions on Privacy

Flutter Android: Unable to fetch in-app products from Google Play

Django-Storages with SFTP: GET-requests fail

How to change the Picker menu text size in SwiftUI?

Action requested: Declare your Ad ID permission

Azure Function Runtime Unreachable when deploying from Azure devops pipeline

How to check if a bot can DM a user

How to enable files and folders permission on iOS

Multipeer Connectivity - Get file transfer(Internet) speed and File Size in Swift 5

Memory efficiency of nested functions in python

Stripe-Android SDK doesn’t comply with the User Data and Mobile Unwanted Software policies of Google Play Store

QUESTION

Flutter Android: Unable to fetch in-app products from Google Play

Asked 2022-Apr-03 at 21:46

I'm currently facing an issue where my Flutter application is unable to fetch consumable in-app products from Google Play store. However, my application is able to fetch all products from the Apple app store.

I can't identify what step I'm missing or what is causing all of my product ids to be not found. I'm using flutter's in_app_purchase module to facilitate in app purchases.

For Android, here are the setup steps I've taken.

  1. I've setup my Google Play Console and Developer Account
  2. Completed all the tasks in the Set up your app section
  3. Generated a keystore file to sign my app keytool -genkey -v -keystore c:\Users\USER_NAME\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias key
  4. Created a file named /android/key.properties that contains a reference to my keystore file. The contents of this file look like the following:
1storePassword=<password from previous step>
2keyPassword=<password from previous step>
3keyAlias=key
4storeFile=<location of the key store file, such as /Users/<user name>/key.jks>
5
  1. Configured my build.gradle file to include the following
1storePassword=<password from previous step>
2keyPassword=<password from previous step>
3keyAlias=key
4storeFile=<location of the key store file, such as /Users/<user name>/key.jks>
5def keystoreProperties = new Properties()
6def keystorePropertiesFile = rootProject.file('key.properties')
7if (keystorePropertiesFile.exists()) {
8   keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
9}
10android {
11   signingConfigs {
12       release {
13           keyAlias keystoreProperties['keyAlias']
14           keyPassword keystoreProperties['keyPassword']
15           storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
16           storePassword keystoreProperties['storePassword']
17       }
18   }
19   buildTypes {
20       release {
21           signingConfig signingConfigs.release
22       }
23   }
24}
25
  1. Created a app bundle and published it under the closed track flutter build appbundle --flavor prod --release. I also let google sign my app "Google signing the app" when I was uploading the artifact.

  2. I've added a tester that is not associated with the developer account and added the same email under "License testing" with the License response "RESPOND_NORMALLY".

  3. I've also created three consumable products and set them to active.

The following shows my code that fetches products

1storePassword=<password from previous step>
2keyPassword=<password from previous step>
3keyAlias=key
4storeFile=<location of the key store file, such as /Users/<user name>/key.jks>
5def keystoreProperties = new Properties()
6def keystorePropertiesFile = rootProject.file('key.properties')
7if (keystorePropertiesFile.exists()) {
8   keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
9}
10android {
11   signingConfigs {
12       release {
13           keyAlias keystoreProperties['keyAlias']
14           keyPassword keystoreProperties['keyPassword']
15           storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
16           storePassword keystoreProperties['storePassword']
17       }
18   }
19   buildTypes {
20       release {
21           signingConfig signingConfigs.release
22       }
23   }
24}
25  List<String> _kProductIds = <String>[
26    'my_app_premium_year_c',
27    'my_app_premium_month_c',
28    'my_app_premium_week_c'
29  ];
30final ProductDetailsResponse productResponse =
31        await InAppPurchase.instance.queryProductDetails(_kProductIds.toSet());
32

The following shows the unexpected results I'm getting. That is all of my products are in the notFoundIDs list. [my_app_premium_year_c, my_app_premium_month_c, my_app_premium_week_c]

I expect the notFoundIDs list to be empty when including the following code print("ProductIDs not found: ${productResponse.notFoundIDs}");

Any hints or suggestions would be much appreciated.

EDIT: The following screenshot shows my in-app products page on the google console. I've covered up certain identifiers to protect my privacy, however the product ids in the screenshot match the product ids in my code. in-app-products

The following screenshot shows my app published under the alpha, closed track. alpha track

ANSWER

Answered 2022-Apr-03 at 21:46

The issue has finally been solved. You need to call InAppPurchase method, isAvailable(), before queryProductDetails() when on the Android platform. I'm not sure why you don't need to do the same when on the IOS platform.

The documentation didn't specify the need for this outright, but let it stand that querying for products AFTER checking if the store is available fixed my issue on Android.

Source https://stackoverflow.com/questions/71668207

QUESTION

Django-Storages with SFTP: GET-requests fail

Asked 2022-Apr-03 at 13:48

I am trying to use django-storages to access my "Hetzner" Storage Box (https://www.hetzner.com/storage/storage-box) using SFTP which should hold media data, i.e. image files which users of my website can upload dynamically.

The corresponding part of my settings.py file looks like:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11

The strange thing is that, when the user uploads an Image, it is placed on the storage space as I can confirm using SFTP. But getting the images from the storage box fails, no Image is displayed. An excerpt from the console:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12

I could figure out that Django is still looking inside my MEDIA_DIR for the files. Againg, the corresponding part of my settings:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12MEDIA_DIR = 'media'
13MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
14MEDIA_URL = '/media/'
15

So in a nutshell: Using SFTP seems to be working for putting files into storage, but getting them again somehow fails.

EDIT: As requested, I am going to provide some more code snippets: models.py:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12MEDIA_DIR = 'media'
13MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
14MEDIA_URL = '/media/'
15class SizeRestrictedImageField(ImageField):
16
17    def __init__(self, *args, **kwargs):
18        self.max_upload_size = kwargs.pop('max_upload_size', 0)
19        super().__init__(*args, **kwargs)
20
21    def clean(self, *args, **kwargs):
22        data = super().clean(*args, **kwargs)
23
24        file = data.file
25        try:
26            if file.size > self.max_upload_size:
27                raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s'
28                        ) % (filesizeformat(self.max_upload_size),
29                        filesizeformat(file.size)))
30        except AttributeError:
31            logger.exception('An Exception occured while checking for max size of image upload. size: `%s`'
32                             , file.size)
33            pass
34
35        return data
36
37
38class ImageModel(models.Model):
39    image = SizeRestrictedImageField(upload_to=POST_PIC_FOLDER, null=True, blank=True,
40                              help_text="Erlaubte Dateitypen: .jpeg, .jpg, .png, .gif", max_upload_size=MAX_IMAGE_SIZE)
41

And my urls.py:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12MEDIA_DIR = 'media'
13MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
14MEDIA_URL = '/media/'
15class SizeRestrictedImageField(ImageField):
16
17    def __init__(self, *args, **kwargs):
18        self.max_upload_size = kwargs.pop('max_upload_size', 0)
19        super().__init__(*args, **kwargs)
20
21    def clean(self, *args, **kwargs):
22        data = super().clean(*args, **kwargs)
23
24        file = data.file
25        try:
26            if file.size > self.max_upload_size:
27                raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s'
28                        ) % (filesizeformat(self.max_upload_size),
29                        filesizeformat(file.size)))
30        except AttributeError:
31            logger.exception('An Exception occured while checking for max size of image upload. size: `%s`'
32                             , file.size)
33            pass
34
35        return data
36
37
38class ImageModel(models.Model):
39    image = SizeRestrictedImageField(upload_to=POST_PIC_FOLDER, null=True, blank=True,
40                              help_text="Erlaubte Dateitypen: .jpeg, .jpg, .png, .gif", max_upload_size=MAX_IMAGE_SIZE)
41urlpatterns = [
42                  path('defaultsite/', defaultsite_view, name='home'),
43                  path('help', help_view, name="help"),
44                  path('user/', include('user.urls')),
45                  path('sowi/', include('sowi.urls')),
46                  path('blog/', include('blog.urls')),
47                  path('chat/', include('chat.urls')),
48                  path('notifications/', include('notifications.urls')),
49                  path('cookies/', include('cookie_consent.urls')),
50                  path('', home_view, name="home"),
51                  path('about/', AboutUsView.as_view(), name="about-us"),
52                  path('impressum/', impressum_view, name="imprint"),
53                  path('privacy/', privacy_view, name='privacy'),
54                  path('privacy/statement/', privacy_statement_view, name='privacy-statement'),
55                  path('agb', agb_view, name="agb")
56              ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL,
57                                                                                           document_root=settings.MEDIA_ROOT)
58

I tried removing the +static(...)-part from my url-patterns, but that didn't seem to solve the issue.

ANSWER

Answered 2021-Sep-06 at 09:00
Check django-storage setup

I feel you may have forgot to migrate your fields in django models ? In django-storage documentation on Github, you have those snippet of code.

From:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12MEDIA_DIR = 'media'
13MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
14MEDIA_URL = '/media/'
15class SizeRestrictedImageField(ImageField):
16
17    def __init__(self, *args, **kwargs):
18        self.max_upload_size = kwargs.pop('max_upload_size', 0)
19        super().__init__(*args, **kwargs)
20
21    def clean(self, *args, **kwargs):
22        data = super().clean(*args, **kwargs)
23
24        file = data.file
25        try:
26            if file.size > self.max_upload_size:
27                raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s'
28                        ) % (filesizeformat(self.max_upload_size),
29                        filesizeformat(file.size)))
30        except AttributeError:
31            logger.exception('An Exception occured while checking for max size of image upload. size: `%s`'
32                             , file.size)
33            pass
34
35        return data
36
37
38class ImageModel(models.Model):
39    image = SizeRestrictedImageField(upload_to=POST_PIC_FOLDER, null=True, blank=True,
40                              help_text="Erlaubte Dateitypen: .jpeg, .jpg, .png, .gif", max_upload_size=MAX_IMAGE_SIZE)
41urlpatterns = [
42                  path('defaultsite/', defaultsite_view, name='home'),
43                  path('help', help_view, name="help"),
44                  path('user/', include('user.urls')),
45                  path('sowi/', include('sowi.urls')),
46                  path('blog/', include('blog.urls')),
47                  path('chat/', include('chat.urls')),
48                  path('notifications/', include('notifications.urls')),
49                  path('cookies/', include('cookie_consent.urls')),
50                  path('', home_view, name="home"),
51                  path('about/', AboutUsView.as_view(), name="about-us"),
52                  path('impressum/', impressum_view, name="imprint"),
53                  path('privacy/', privacy_view, name='privacy'),
54                  path('privacy/statement/', privacy_statement_view, name='privacy-statement'),
55                  path('agb', agb_view, name="agb")
56              ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL,
57                                                                                           document_root=settings.MEDIA_ROOT)
58photo = models.FileField(
59    storage=FileSystemStorage(location=settings.MEDIA_ROOT),
60    upload_to='photos',
61)
62

to:

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12MEDIA_DIR = 'media'
13MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
14MEDIA_URL = '/media/'
15class SizeRestrictedImageField(ImageField):
16
17    def __init__(self, *args, **kwargs):
18        self.max_upload_size = kwargs.pop('max_upload_size', 0)
19        super().__init__(*args, **kwargs)
20
21    def clean(self, *args, **kwargs):
22        data = super().clean(*args, **kwargs)
23
24        file = data.file
25        try:
26            if file.size > self.max_upload_size:
27                raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s'
28                        ) % (filesizeformat(self.max_upload_size),
29                        filesizeformat(file.size)))
30        except AttributeError:
31            logger.exception('An Exception occured while checking for max size of image upload. size: `%s`'
32                             , file.size)
33            pass
34
35        return data
36
37
38class ImageModel(models.Model):
39    image = SizeRestrictedImageField(upload_to=POST_PIC_FOLDER, null=True, blank=True,
40                              help_text="Erlaubte Dateitypen: .jpeg, .jpg, .png, .gif", max_upload_size=MAX_IMAGE_SIZE)
41urlpatterns = [
42                  path('defaultsite/', defaultsite_view, name='home'),
43                  path('help', help_view, name="help"),
44                  path('user/', include('user.urls')),
45                  path('sowi/', include('sowi.urls')),
46                  path('blog/', include('blog.urls')),
47                  path('chat/', include('chat.urls')),
48                  path('notifications/', include('notifications.urls')),
49                  path('cookies/', include('cookie_consent.urls')),
50                  path('', home_view, name="home"),
51                  path('about/', AboutUsView.as_view(), name="about-us"),
52                  path('impressum/', impressum_view, name="imprint"),
53                  path('privacy/', privacy_view, name='privacy'),
54                  path('privacy/statement/', privacy_statement_view, name='privacy-statement'),
55                  path('agb', agb_view, name="agb")
56              ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL,
57                                                                                           document_root=settings.MEDIA_ROOT)
58photo = models.FileField(
59    storage=FileSystemStorage(location=settings.MEDIA_ROOT),
60    upload_to='photos',
61)
62photo = models.FileField(
63    upload_to='photos',
64)
65

Could that be it ? (as mention in the comment, having some snippet of code would greatly help.

SFTP access

Django-storage act has a proxy to save you files some place. I can be a s3 bucket, an http cdn like. Or in you case a SFTP server.

with other back-end supporting the HTTP protocol it is fairly easy to get the file back. As the back-end will provide you with a link directly to the content you stored.

For SFTP, this is going to be different, web pages does not natively support FTP protocol. So in order to access the file, you will have to create a proxy layer between your web pages, and the FTP server.

1DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
2SFTP_STORAGE_HOST = 'username.your-storagebox.de'
3SFTP_STORAGE_ROOT = '/media'
4
5SFTP_STORAGE_PARAMS = {
6'username': 'username',
7'password': 'password',
8'allow_agent': False,
9'look_for_keys': False,
10}
11[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962
12MEDIA_DIR = 'media'
13MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
14MEDIA_URL = '/media/'
15class SizeRestrictedImageField(ImageField):
16
17    def __init__(self, *args, **kwargs):
18        self.max_upload_size = kwargs.pop('max_upload_size', 0)
19        super().__init__(*args, **kwargs)
20
21    def clean(self, *args, **kwargs):
22        data = super().clean(*args, **kwargs)
23
24        file = data.file
25        try:
26            if file.size > self.max_upload_size:
27                raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s'
28                        ) % (filesizeformat(self.max_upload_size),
29                        filesizeformat(file.size)))
30        except AttributeError:
31            logger.exception('An Exception occured while checking for max size of image upload. size: `%s`'
32                             , file.size)
33            pass
34
35        return data
36
37
38class ImageModel(models.Model):
39    image = SizeRestrictedImageField(upload_to=POST_PIC_FOLDER, null=True, blank=True,
40                              help_text="Erlaubte Dateitypen: .jpeg, .jpg, .png, .gif", max_upload_size=MAX_IMAGE_SIZE)
41urlpatterns = [
42                  path('defaultsite/', defaultsite_view, name='home'),
43                  path('help', help_view, name="help"),
44                  path('user/', include('user.urls')),
45                  path('sowi/', include('sowi.urls')),
46                  path('blog/', include('blog.urls')),
47                  path('chat/', include('chat.urls')),
48                  path('notifications/', include('notifications.urls')),
49                  path('cookies/', include('cookie_consent.urls')),
50                  path('', home_view, name="home"),
51                  path('about/', AboutUsView.as_view(), name="about-us"),
52                  path('impressum/', impressum_view, name="imprint"),
53                  path('privacy/', privacy_view, name='privacy'),
54                  path('privacy/statement/', privacy_statement_view, name='privacy-statement'),
55                  path('agb', agb_view, name="agb")
56              ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL,
57                                                                                           document_root=settings.MEDIA_ROOT)
58photo = models.FileField(
59    storage=FileSystemStorage(location=settings.MEDIA_ROOT),
60    upload_to='photos',
61)
62photo = models.FileField(
63    upload_to='photos',
64)
65@action(methods=['get'], detail=True)
66def download(self, request, pk=None):
67
68    try:
69        obj = ImageModel.objects.get(id=pk)
70    except ImageModel.DoesNotExist:
71        raise Http404
72
73    # with some SFTP client
74    # 1. check the file exist
75    # 2. pull the file from the server
76    # 3. attach it to the response with the proper header
77    stream = sftp_client.open(obj.file.name)
78    file = stream.read()
79
80    type, encoding = mimetypes.guess_type(obj.file.name)
81    response = HttpResponse(file, content_type=type)
82    response['Content-Disposition'] = u'attachment; filename="{filename}'.format(
83            filename=obj.file.name)
84        return response
85    raise Http404
86

Source https://stackoverflow.com/questions/69050396

QUESTION

How to change the Picker menu text size in SwiftUI?

Asked 2022-Mar-18 at 12:01

I have a Picker of style Menu and I need to change its text size (the blue text), I tried the .font(.largeTitle) modifier but it didn't work.

1enum Privacy: String, Identifiable, CaseIterable {
2    case open = "Open"
3    case closed = "Closed"
4    var id: String { self.rawValue }
5}
6
7struct ContentView: View {
8    @State var selection = Privacy.open
9    var body: some View {
10        Picker("Privacy", selection: $selection) {
11            ForEach(Privacy.allCases) { value in
12                Text(value.rawValue)
13                    .tag(value)
14                    .font(.largeTitle)
15            }
16        }
17        .font(.largeTitle)
18        .pickerStyle(.menu)
19    }
20}
21

ANSWER

Answered 2022-Jan-06 at 19:21

Remove the .menu style and just wrap it in Menu instead, with a custom label:

1enum Privacy: String, Identifiable, CaseIterable {
2    case open = "Open"
3    case closed = "Closed"
4    var id: String { self.rawValue }
5}
6
7struct ContentView: View {
8    @State var selection = Privacy.open
9    var body: some View {
10        Picker("Privacy", selection: $selection) {
11            ForEach(Privacy.allCases) { value in
12                Text(value.rawValue)
13                    .tag(value)
14                    .font(.largeTitle)
15            }
16        }
17        .font(.largeTitle)
18        .pickerStyle(.menu)
19    }
20}
21Menu {
22    Picker(selection: $selection) {
23        ForEach(Privacy.allCases) { value in
24            Text(value.rawValue)
25                .tag(value)
26                .font(.largeTitle)
27        }
28    } label: {}
29} label: {
30    Text("Privacy")
31        .font(.largeTitle)
32}
33

Source https://stackoverflow.com/questions/70612400

QUESTION

Action requested: Declare your Ad ID permission

Asked 2022-Mar-15 at 13:37

Today i have got this email:

Last July, we announced Advertising policy changes to help bolster security and privacy. We added new restrictions on identifiers used by apps that target children. When users choose to delete their advertising ID in order to opt out of personalization advertising, developers will receive a string of zeros instead of the identifier if they attempt to access the identifier. This behavior will extend to phones, tablets, and Android TV starting April 1, 2022. We also announced that you need to declare an AD_ID permission when you update your app targeting API level to 31 (Android 12). Today, we are sharing that we will give developers more time to ease the transition. We will require this permission declaration when your apps are able to target Android 13 instead of starting with Android 12.

Action Items If you use an advertising ID, you must declare the AD_ID Permission when your app targets Android 13 or above. Apps that don’t declare the permission will get a string of zeros. Note: You’ll be able to target Android 13 later this year. If your app uses an SDK that has declared the Ad ID permission, it will acquire the permission declaration through manifest merge. If your app’s target audience includes children, you must not transmit Android Advertising ID (AAID) from children or users of unknown age.

My app is not using the Advertising ID. Should i declare the AD_ID Permission in Manifest or not?

ANSWER

Answered 2022-Mar-14 at 20:51

Google describe here how to solve

https://support.google.com/googleplay/android-developer/answer/6048248?hl=en

Add in manifest

1<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
2

Source https://stackoverflow.com/questions/71473553

QUESTION

Azure Function Runtime Unreachable when deploying from Azure devops pipeline

Asked 2022-Feb-28 at 17:26

Thanks for taking the time to read. My current setup is as follows:

I have an azure function service up and running, an az function project in visual studio (which I have tested and it runs without issue), a build pipeline in azure devops that deploys a docker image with my function project to an azure container registry.

My problem:

When I try to setup my function service for CI/CD from my devops pipeline, I get the following error on the "functions" tab on my app: "Azure Functions runtime is unreachable". Also none of the functions from my code are listed. In the deployment center however, I get a message "Deployed successfully to production", and it shows my built docker container image name.

Troubleshooting:

In the deployment center of my function app (in the az portal), I set my app to read directly from the azure container registry (using the exact same docker image that my pipeline built earlier), and that worked perfectly - deployment successful and I could see my individual functions name. When I switched back to CI/CD deployment however I got the same problem as earlier.

Trying to see if anyone has had the same problem or could suggest a path forward for getting CI/CD integration working.

I pasted my yaml file below with some names changed for privacy.

1trigger:
2- master
3
4pool:
5  vmImage: 'ubuntu-latest'
6
7variables:
8  project: 'myProjectName'
9  environment: 'prod'
10  dockerfile: '**/Dockerfile'
11  azureResource: 'myScraperFunction'
12  imageName: myScraperFunction
13
14steps:
15- task: Docker@2
16  displayName: Build
17  inputs:
18    command: build
19    containerRegistry: $(azureContainerRegistryName)
20    repository: '$(Build.Repository.Name)-$(project)'
21    tags: '$(Build.BuildNumber)-$(environment)'
22    Dockerfile: $(dockerfile)
23    buildContext: '$(Build.SourcesDirectory)'
24    arguments: --build-arg PAT=$(System.AccessToken)
25
26- task: Docker@2
27  displayName: Push
28  inputs:
29    command: push
30    containerRegistry: $(azureContainerRegistryName)
31    repository: '$(Build.Repository.Name)-$(project)'
32    tags: '$(Build.BuildNumber)-$(environment)'
33    Dockerfile: $(dockerfile)       
34
35- task: AzureFunctionAppContainer@1
36  displayName: 'Azure Function App on Container Deploy: $(azureResource)'
37  inputs:
38    azureSubscription: '$(azureSubscription)'
39    appName: $(azureResource)
40    imageName: '$(azureContainerRegistry)/$(imageName):$(Build.BuildNumber)-$(environment)'
41

ANSWER

Answered 2022-Feb-28 at 17:26

Configuration/typing issue in yml file. Repository name during build step was hard-coded (I didn't use a variable like what was posted above) and did not match repo name in deploy step because of a spelling error.

Source https://stackoverflow.com/questions/68785291

QUESTION

How to check if a bot can DM a user

Asked 2022-Jan-22 at 22:03

If a user has the privacy setting "Allow direct messages from server members" turned off and a discord bot calls

1await user.dm_channel.send("Hello there")
2

You'll get this error:

1await user.dm_channel.send("Hello there")
2discord.errors.Forbidden: 403 Forbidden (error code: 50007): Cannot send messages to this user
3

I would like to check whether I can message a user without sending them a message. Trying to send a message and catching this error does not work for me, because I don't want a message to get sent in the event that the bot is allowed to message.

I have tried this:

1await user.dm_channel.send("Hello there")
2discord.errors.Forbidden: 403 Forbidden (error code: 50007): Cannot send messages to this user
3print(user.dm_channel.permissions_for(bot).send_messages)
4

but it always returns True, even if the message is not permitted.

I have also tried this:

1await user.dm_channel.send("Hello there")
2discord.errors.Forbidden: 403 Forbidden (error code: 50007): Cannot send messages to this user
3print(user.dm_channel.permissions_for(bot).send_messages)
4channel = await user.create_dm()
5if channel is None:
6    ...
7

but unfortunately, it seems that "has permission to message user" and "has permission to create a dm channel" are considered different.

EDIT

To clarify the exact usage since there seems to be a bit of confusion, take this example. There is a server, and 3 users in question: Me, My Bot, and Steve. Steve has "Allow direct messages from server members" checked off.

The bot has a command called !newgame which accepts a list of users and starts a game amongst them, which involves DMing some of the members of the game. Because of Steve's privacy settings, he cannot play the game (since the bot will need to message him). If I do

1await user.dm_channel.send("Hello there")
2discord.errors.Forbidden: 403 Forbidden (error code: 50007): Cannot send messages to this user
3print(user.dm_channel.permissions_for(bot).send_messages)
4channel = await user.create_dm()
5if channel is None:
6    ...
7!newgame @DJMcMayhem @Steve
8

I'd like to provide a response like:

1await user.dm_channel.send("Hello there")
2discord.errors.Forbidden: 403 Forbidden (error code: 50007): Cannot send messages to this user
3print(user.dm_channel.permissions_for(bot).send_messages)
4channel = await user.create_dm()
5if channel is None:
6    ...
7!newgame @DJMcMayhem @Steve
8> I can't start a game with that list of users because @Steve has the wrong privacy settings.
9

But as far as I know right now, the only way to find out if Steve can play is by first attempting to message every user, which I'd like to avoid.

ANSWER

Answered 2022-Jan-22 at 22:03
Explanation

You can generate a Bad Request to the dm_channel. This can be accomplished by setting content to None, for example.

If it returns with 400 Bad Request, you can DM them. If it returns with 403 Forbidden, you can't.

Code
1await user.dm_channel.send("Hello there")
2discord.errors.Forbidden: 403 Forbidden (error code: 50007): Cannot send messages to this user
3print(user.dm_channel.permissions_for(bot).send_messages)
4channel = await user.create_dm()
5if channel is None:
6    ...
7!newgame @DJMcMayhem @Steve
8> I can't start a game with that list of users because @Steve has the wrong privacy settings.
9async def can_dm_user(user: discord.User) -> bool:
10    ch = user.dm_channel
11    if ch is None:
12        ch = await user.create_dm()
13
14    try:
15        await ch.send()
16    except discord.Forbidden:
17        return False
18    except discord.HTTPException:
19        return True
20
21

Source https://stackoverflow.com/questions/70660854

QUESTION

How to enable files and folders permission on iOS

Asked 2022-Jan-18 at 22:24

I am trying to download a file using AlamoFire and save it to a downloads directory of the user's choice (like safari). However, whenever I set the download directory to a folder outside of my app's documents, I get the following error (on a real iOS device):

downloadedFileMoveFailed(error: Error Domain=NSCocoaErrorDomain Code=513 "“CFNetworkDownload_dlIcno.tmp” couldn’t be moved because you don’t have permission to access “Downloads”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, NSUserStringVariant=(Move), NSDestinationFilePath=/private/var/mobile/Containers/Shared/AppGroup/E6303CBC-62A3-4206-9C84-E37041894DEC/File Provider Storage/Downloads/100MB.bin, NSFilePath=/private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, NSUnderlyingError=0x281d045d0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}, source: file:///private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, destination: file:///private/var/mobile/Containers/Shared/AppGroup/E6303CBC-62A3-4206-9C84-E37041894DEC/File%20Provider%20Storage/Downloads/100MB.bin)

The summary of that error is that I don't have permission to access the folder I just granted access to.

Here's my attached code:

1import SwiftUI
2import UniformTypeIdentifiers
3import Alamofire
4
5struct ContentView: View {
6    @AppStorage("downloadsDirectory") var downloadsDirectory = ""
7    
8    @State private var showFileImporter = false
9    
10    var body: some View {
11        VStack {
12            Button("Set downloads directory") {
13                showFileImporter.toggle()
14            }
15            
16            Button("Save to downloads directory") {
17                Task {
18                    do {
19                        let destination: DownloadRequest.Destination = { _, response in
20                            let documentsURL = URL(string: downloadsDirectory)!
21                            let suggestedName = response.suggestedFilename ?? "unknown"
22
23                            let fileURL = documentsURL.appendingPathComponent(suggestedName)
24
25                            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
26                        }
27
28                        let _ = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value
29                    } catch {
30                        print("Downloading error!: \(error)")
31                    }
32                }
33            }
34        }
35        .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in
36            switch result {
37            case .success(let url):
38                downloadsDirectory = url.absoluteString
39            case .failure(let error):
40                print("Download picker error: \(error)")
41            }
42        }
43    }
44}
45

To reproduce (Run on a REAL iOS device!):

  1. Click the Set downloads directory button to On my iPhone
  2. Click the Save to downloads directory button
  3. Error occurs

Upon further investigation, I found that safari uses the Files and Folders privacy permission (Located in Settings > Privacy > Files and folders on an iPhone) to access folders outside the app sandbox (This link for the image of what I'm talking about). I scoured the web as much as I can and I couldn't find any documentation for this exact permission.

I have seen non-apple apps (such as VLC) use this permission, but I cannot figure out how it's granted.

I tried enabling the following plist properties, but none of them work (because I later realized these are for macOS only)

1import SwiftUI
2import UniformTypeIdentifiers
3import Alamofire
4
5struct ContentView: View {
6    @AppStorage("downloadsDirectory") var downloadsDirectory = ""
7    
8    @State private var showFileImporter = false
9    
10    var body: some View {
11        VStack {
12            Button("Set downloads directory") {
13                showFileImporter.toggle()
14            }
15            
16            Button("Save to downloads directory") {
17                Task {
18                    do {
19                        let destination: DownloadRequest.Destination = { _, response in
20                            let documentsURL = URL(string: downloadsDirectory)!
21                            let suggestedName = response.suggestedFilename ?? "unknown"
22
23                            let fileURL = documentsURL.appendingPathComponent(suggestedName)
24
25                            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
26                        }
27
28                        let _ = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value
29                    } catch {
30                        print("Downloading error!: \(error)")
31                    }
32                }
33            }
34        }
35        .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in
36            switch result {
37            case .success(let url):
38                downloadsDirectory = url.absoluteString
39            case .failure(let error):
40                print("Download picker error: \(error)")
41            }
42        }
43    }
44}
45<key>NSDocumentsFolderUsageDescription</key>
46<string>App wants to access your documents folder</string>
47<key>NSDownloadsFolderUsageDescription</key>
48<string>App wants to access your downloads folder</string>
49<key>LSSupportsOpeningDocumentsInPlace</key>
50<true/>
51<key>UIFileSharingEnabled</key>
52<true/>
53

Can someone please help me figure out how to grant the files and folder permission and explain what it does? I would really appreciate the help.

ANSWER

Answered 2022-Jan-18 at 22:24

After some research, I stumbled onto this Apple documentation page (which wasn't found after my hours of google searching when I posted this question)

https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories

Navigate to the Save the URL as a Bookmark part of the article.

Utilizing a SwiftUI fileImporter gives one-time read/write access to the user-selected directory. To persist this read/write access, I had to make a bookmark and store it somewhere to access later.

Since I only needed one bookmark for the user's downloads directory, I saved it in UserDefaults (a bookmark is very small in terms of size).

When saving a bookmark, the app is added into the Files and folders in the user's settings, so the user can revoke file permissions for the app immediately (hence all the guard statements in my code snippet).

Here's the code snippet which I used and with testing, downloading does persist across app launches and multiple downloads.

1import SwiftUI
2import UniformTypeIdentifiers
3import Alamofire
4
5struct ContentView: View {
6    @AppStorage("downloadsDirectory") var downloadsDirectory = ""
7    
8    @State private var showFileImporter = false
9    
10    var body: some View {
11        VStack {
12            Button("Set downloads directory") {
13                showFileImporter.toggle()
14            }
15            
16            Button("Save to downloads directory") {
17                Task {
18                    do {
19                        let destination: DownloadRequest.Destination = { _, response in
20                            let documentsURL = URL(string: downloadsDirectory)!
21                            let suggestedName = response.suggestedFilename ?? "unknown"
22
23                            let fileURL = documentsURL.appendingPathComponent(suggestedName)
24
25                            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
26                        }
27
28                        let _ = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value
29                    } catch {
30                        print("Downloading error!: \(error)")
31                    }
32                }
33            }
34        }
35        .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in
36            switch result {
37            case .success(let url):
38                downloadsDirectory = url.absoluteString
39            case .failure(let error):
40                print("Download picker error: \(error)")
41            }
42        }
43    }
44}
45<key>NSDocumentsFolderUsageDescription</key>
46<string>App wants to access your documents folder</string>
47<key>NSDownloadsFolderUsageDescription</key>
48<string>App wants to access your downloads folder</string>
49<key>LSSupportsOpeningDocumentsInPlace</key>
50<true/>
51<key>UIFileSharingEnabled</key>
52<true/>
53import SwiftUI
54import UniformTypeIdentifiers
55import Alamofire
56
57struct ContentView: View {
58    @AppStorage("bookmarkData") var downloadsBookmark: Data?
59    
60    @State private var showFileImporter = false
61    
62    var body: some View {
63        VStack {
64            Button("Set downloads directory") {
65                showFileImporter.toggle()
66            }
67            
68            Button("Save to downloads directory") {
69                Task {
70                    do {
71                        let destination: DownloadRequest.Destination = { _, response in
72                            // Save to a temp directory in app documents
73                            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("Downloads")
74                            let suggestedName = response.suggestedFilename ?? "unknown"
75
76                            let fileURL = documentsURL.appendingPathComponent(suggestedName)
77
78                            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
79                        }
80
81                        let tempUrl = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value
82                        
83                        // Get the bookmark data from the AppStorage call
84                        guard let bookmarkData = downloadsBookmark else {
85                            return
86                        }
87                        var isStale = false
88                        let downloadsUrl = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
89                        
90                        guard !isStale else {
91                            // Log that the bookmark is stale
92                            
93                            return
94                        }
95                        
96                        // Securely access the URL from the bookmark data
97                        guard downloadsUrl.startAccessingSecurityScopedResource() else {
98                            print("Can't access security scoped resource")
99                            
100                            return
101                        }
102                        
103                        // We have to stop accessing the resource no matter what
104                        defer { downloadsUrl.stopAccessingSecurityScopedResource() }
105                        
106                        do {
107                            try FileManager.default.moveItem(at: tempUrl, to: downloadsUrl.appendingPathComponent(tempUrl.lastPathComponent))
108                        } catch {
109                            print("Move error: \(error)")
110                        }
111                    } catch {
112                        print("Downloading error!: \(error)")
113                    }
114                }
115            }
116        }
117        .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in
118            switch result {
119            case .success(let url):
120                // Securely access the URL to save a bookmark
121                guard url.startAccessingSecurityScopedResource() else {
122                    // Handle the failure here.
123                    return
124                }
125                
126                // We have to stop accessing the resource no matter what
127                defer { url.stopAccessingSecurityScopedResource() }
128                
129                do {
130                    // Make sure the bookmark is minimal!
131                    downloadsBookmark = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
132                } catch {
133                    print("Bookmark error \(error)")
134                }
135            case .failure(let error):
136                print("Importer error: \(error)")
137            }
138        }
139    }
140}
141

Source https://stackoverflow.com/questions/70750276

QUESTION

Multipeer Connectivity - Get file transfer(Internet) speed and File Size in Swift 5

Asked 2022-Jan-13 at 13:19

I am transferring photo peer to peer. All things works fine but I am not able to get the photo(file) transfer speed i.g internet speed. Like MB the file is transferred. Second I want to fetch the size of that file.

We are passing photo in data format using MCSession

Due to privacy I cannot add the project code here but I will share the refrence github project that I followed. In project I am passing string and In my case its Photo. All things are same.

I checked in Stackoverflow but not found any accurate answer!

Reference Project Link: https://github.com/YogeshPateliOS/MultipeerConnectivity-.git

Thank You!

ANSWER

Answered 2022-Jan-13 at 13:19

TLDR: If you do not want to read the long explanation and get straight to the code, all the ideas below are brought together and can be tested by downloading my public repository which has comments to explain all of this.

So here are my suggestions on how you can achieve this

After reviewing your code, I see that you are using the following function to send data

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2

There is nothing wrong with this and you can indeed convert your UIImage to a Data object and send it this way, it will work.

However I don't think you can track progress and MultiPeer does not give you any delegates to track progress using this method.

Instead you are left with two other options. You could use

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2func session(_ session: MCSession, 
3             didFinishReceivingResourceWithName resourceName: String, 
4             fromPeer peerID: MCPeerID, 
5             at localURL: URL?, 
6             withError error: Error?)
7

Or you could use

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2func session(_ session: MCSession, 
3             didFinishReceivingResourceWithName resourceName: String, 
4             fromPeer peerID: MCPeerID, 
5             at localURL: URL?, 
6             withError error: Error?)
7func startStream(withName streamName: String, 
8                 toPeer peerID: MCPeerID) throws -> OutputStream
9

I am going to use the first option as that is more straightforward however I think the stream option will give you better results. You can read up on both the options here:

Send Resource (we will implement this one)

Streaming

Step 1

I made some UI updates to your original code by adding a UIImageView to show the transferred image to the advertiser(guest) and a UIButton to start the file transfer from the browser(host) MultiPeer Connectivity File Transfer Progress Sample Project Storyboard

The UIImageView has an outlet named @IBOutlet weak var imageView: UIImageView! and an action for the UIButton @IBAction func sendImageAsResource(_ sender: Any)

I have also added an image called image2.jpg to the project which we will send from the host to the guest.

Step 2

I also declared few additional variables

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2func session(_ session: MCSession, 
3             didFinishReceivingResourceWithName resourceName: String, 
4             fromPeer peerID: MCPeerID, 
5             at localURL: URL?, 
6             withError error: Error?)
7func startStream(withName streamName: String, 
8                 toPeer peerID: MCPeerID) throws -> OutputStream
9// Progress variable that needs to store the progress of the file transfer
10var fileTransferProgress: Progress?
11    
12// Timer that will be used to check the file transfer progress
13var checkProgressTimer: Timer?
14
15// Used by the host to track bytes to receive
16var bytesExpectedToExchange = 0
17    
18// Used to track the time taken in transfer, this is for testing purposes.
19// You might get more reliable results using Date to track time
20var transferTimeElapsed = 0.0
21

Step 3

Set up the host and guest as normal by tapping Guest and Host buttons respectively. After that, tap the Send image as resource button on the host and the action is implemented as follows for the host:

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2func session(_ session: MCSession, 
3             didFinishReceivingResourceWithName resourceName: String, 
4             fromPeer peerID: MCPeerID, 
5             at localURL: URL?, 
6             withError error: Error?)
7func startStream(withName streamName: String, 
8                 toPeer peerID: MCPeerID) throws -> OutputStream
9// Progress variable that needs to store the progress of the file transfer
10var fileTransferProgress: Progress?
11    
12// Timer that will be used to check the file transfer progress
13var checkProgressTimer: Timer?
14
15// Used by the host to track bytes to receive
16var bytesExpectedToExchange = 0
17    
18// Used to track the time taken in transfer, this is for testing purposes.
19// You might get more reliable results using Date to track time
20var transferTimeElapsed = 0.0
21// A new action added to send the image stored in the bundle
22@IBAction func sendImageAsResource(_ sender: Any) 
23{        
24        // Call local function created
25        sendImageAsResource()
26}
27
28func sendImageAsResource()
29{
30        // 1. Get the url of the image in the project bundle.
31        // Change this if your image is hosted in your documents directory
32        // or elsewhere.
33        //
34        // 2. Get all the connected peers. For testing purposes I am only
35        // getting the first peer, you might need to loop through all your
36        // connected peers and send the files individually.
37        guard let imageURL = Bundle.main.url(forResource: "image2",
38                                             withExtension: "jpg"),
39              let guestPeerID = mcSession.connectedPeers.first else {
40            return
41        }
42        
43        // Retrieve the file size of the image
44        if let fileSizeToTransfer = getFileSize(atURL: imageURL)
45        {
46            bytesExpectedToExchange = fileSizeToTransfer
47            
48            // Put the file size in a dictionary
49            let fileTransferMeta = ["fileSize": bytesExpectedToExchange]
50            
51            // Convert the dictionary to a data object in order to send it via
52            // MultiPeer
53            let encoder = JSONEncoder()
54            
55            if let JSONData = try? encoder.encode(fileTransferMeta)
56            {
57                // Send the file size to the guest users
58                try? mcSession.send(JSONData, toPeers: mcSession.connectedPeers,
59                                    with: .reliable)
60            }
61        }
62        
63        // Ideally for best reliability, you will want to develop some logic
64        // for the guest to respond that it has received the file size and then
65        // you should initiate the transfer to that peer only after you receive
66        // this confirmation. For now, I just add a delay so that I am highly
67        // certain the guest has received this data for testing purposes
68        DispatchQueue.main.asyncAfter(deadline: .now() + 1)
69        { [weak self] in
70            self?.initiateFileTransfer(ofImage: imageURL, to: guestPeerID)
71        }
72    }
73    
74func initiateFileTransfer(ofImage imageURL: URL, to guestPeerID: MCPeerID)
75{
76        // Initialize and fire a timer to check the status of the file
77        // transfer every 0.1 second
78        checkProgressTimer = Timer.scheduledTimer(timeInterval: 0.1,
79                                                  target: self,
80                                                  selector: #selector(updateProgressStatus),
81                                                  userInfo: nil,
82                                                  repeats: true)
83        
84        // Call the sendResource function and send the image from the bundle
85        // keeping hold of the returned progress object which we need to keep checking
86        // using the timer
87        fileTransferProgress = mcSession.sendResource(at: imageURL,
88                                          withName: "image2.jpg",
89                                          toPeer: guestPeerID,
90                                          withCompletionHandler: { (error) in
91                                            
92                                            // Handle errors
93                                            if let error = error as NSError?
94                                            {
95                                                print("Error: \(error.userInfo)")
96                                                print("Error: \(error.localizedDescription)")
97                                            }
98                                            
99                                          })
100}
101
102func getFileSize(atURL url: URL) -> Int?
103{
104        let urlResourceValue = try? url.resourceValues(forKeys: [.fileSizeKey])
105        
106        return urlResourceValue?.fileSize
107}
108

Step 4

This next function is used by the host and the guest. The guest will make side of things will make sense later on, however for the host, in step 3, you have stored a progress object after initiating the file transfer and you have launched a timer to fire every 0.1 seconds, so now implement the timer to query this progress object to display the progress and data transfer status on the host side in the UILabel

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2func session(_ session: MCSession, 
3             didFinishReceivingResourceWithName resourceName: String, 
4             fromPeer peerID: MCPeerID, 
5             at localURL: URL?, 
6             withError error: Error?)
7func startStream(withName streamName: String, 
8                 toPeer peerID: MCPeerID) throws -> OutputStream
9// Progress variable that needs to store the progress of the file transfer
10var fileTransferProgress: Progress?
11    
12// Timer that will be used to check the file transfer progress
13var checkProgressTimer: Timer?
14
15// Used by the host to track bytes to receive
16var bytesExpectedToExchange = 0
17    
18// Used to track the time taken in transfer, this is for testing purposes.
19// You might get more reliable results using Date to track time
20var transferTimeElapsed = 0.0
21// A new action added to send the image stored in the bundle
22@IBAction func sendImageAsResource(_ sender: Any) 
23{        
24        // Call local function created
25        sendImageAsResource()
26}
27
28func sendImageAsResource()
29{
30        // 1. Get the url of the image in the project bundle.
31        // Change this if your image is hosted in your documents directory
32        // or elsewhere.
33        //
34        // 2. Get all the connected peers. For testing purposes I am only
35        // getting the first peer, you might need to loop through all your
36        // connected peers and send the files individually.
37        guard let imageURL = Bundle.main.url(forResource: "image2",
38                                             withExtension: "jpg"),
39              let guestPeerID = mcSession.connectedPeers.first else {
40            return
41        }
42        
43        // Retrieve the file size of the image
44        if let fileSizeToTransfer = getFileSize(atURL: imageURL)
45        {
46            bytesExpectedToExchange = fileSizeToTransfer
47            
48            // Put the file size in a dictionary
49            let fileTransferMeta = ["fileSize": bytesExpectedToExchange]
50            
51            // Convert the dictionary to a data object in order to send it via
52            // MultiPeer
53            let encoder = JSONEncoder()
54            
55            if let JSONData = try? encoder.encode(fileTransferMeta)
56            {
57                // Send the file size to the guest users
58                try? mcSession.send(JSONData, toPeers: mcSession.connectedPeers,
59                                    with: .reliable)
60            }
61        }
62        
63        // Ideally for best reliability, you will want to develop some logic
64        // for the guest to respond that it has received the file size and then
65        // you should initiate the transfer to that peer only after you receive
66        // this confirmation. For now, I just add a delay so that I am highly
67        // certain the guest has received this data for testing purposes
68        DispatchQueue.main.asyncAfter(deadline: .now() + 1)
69        { [weak self] in
70            self?.initiateFileTransfer(ofImage: imageURL, to: guestPeerID)
71        }
72    }
73    
74func initiateFileTransfer(ofImage imageURL: URL, to guestPeerID: MCPeerID)
75{
76        // Initialize and fire a timer to check the status of the file
77        // transfer every 0.1 second
78        checkProgressTimer = Timer.scheduledTimer(timeInterval: 0.1,
79                                                  target: self,
80                                                  selector: #selector(updateProgressStatus),
81                                                  userInfo: nil,
82                                                  repeats: true)
83        
84        // Call the sendResource function and send the image from the bundle
85        // keeping hold of the returned progress object which we need to keep checking
86        // using the timer
87        fileTransferProgress = mcSession.sendResource(at: imageURL,
88                                          withName: "image2.jpg",
89                                          toPeer: guestPeerID,
90                                          withCompletionHandler: { (error) in
91                                            
92                                            // Handle errors
93                                            if let error = error as NSError?
94                                            {
95                                                print("Error: \(error.userInfo)")
96                                                print("Error: \(error.localizedDescription)")
97                                            }
98                                            
99                                          })
100}
101
102func getFileSize(atURL url: URL) -> Int?
103{
104        let urlResourceValue = try? url.resourceValues(forKeys: [.fileSizeKey])
105        
106        return urlResourceValue?.fileSize
107}
108/// Function fired by the local checkProgressTimer object used to track the progress of the file transfer
109/// Function fired by the local checkProgressTimer object used to track the progress of the file transfer
110@objc
111func updateProgressStatus()
112{
113        // Update the time elapsed. As mentioned earlier, a more reliable approach
114        // might be to compare the time of a Date object from when the
115        // transfer started to the time of a current Date object
116        transferTimeElapsed += 0.1
117        
118        // Verify the progress variable is valid
119        if let progress = fileTransferProgress
120        {
121            // Convert the progress into a percentage
122            let percentCompleted = 100 * progress.fractionCompleted
123            
124            // Calculate the data exchanged sent in MegaBytes
125            let dataExchangedInMB = (Double(bytesExpectedToExchange)
126                                     * progress.fractionCompleted) / 1000000
127            
128            // We have exchanged 'dataExchangedInMB' MB of data in 'transferTimeElapsed'
129            // seconds. So we have to calculate how much data will be exchanged in 1 second
130            // using cross multiplication
131            // For example:
132            // 2 MB in 0.5s
133            //  ?   in  1s
134            // MB/s = (1 x 2) / 0.5 = 4 MB/s
135            let megabytesPerSecond = (1 * dataExchangedInMB) / transferTimeElapsed
136            
137            // Convert dataExchangedInMB into a string rounded to 2 decimal places
138            let dataExchangedInMBString = String(format: "%.2f", dataExchangedInMB)
139            
140            // Convert megabytesPerSecond into a string rounded to 2 decimal places
141            let megabytesPerSecondString = String(format: "%.2f", megabytesPerSecond)
142            
143            // Update the progress an data exchanged on the UI
144            numberLabel.text = "\(percentCompleted.rounded())% - \(dataExchangedInMBString) MB @ \(megabytesPerSecondString) MB/s"
145            
146            // This is mostly useful on the browser side to check if the file transfer
147            // is complete so that we can safely deinit the timer, reset vars and update the UI
148            if percentCompleted >= 100
149            {
150                numberLabel.text = "Transfer complete!"
151                checkProgressTimer?.invalidate()
152                checkProgressTimer = nil
153                transferTimeElapsed = 0.0
154            }
155        }
156}
157

Step 5

Handle the receiving of the file on the receiver (guest) side by implementing the following delegate methods

1func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode)
2func session(_ session: MCSession, 
3             didFinishReceivingResourceWithName resourceName: String, 
4             fromPeer peerID: MCPeerID, 
5             at localURL: URL?, 
6             withError error: Error?)
7func startStream(withName streamName: String, 
8                 toPeer peerID: MCPeerID) throws -> OutputStream
9// Progress variable that needs to store the progress of the file transfer
10var fileTransferProgress: Progress?
11    
12// Timer that will be used to check the file transfer progress
13var checkProgressTimer: Timer?
14
15// Used by the host to track bytes to receive
16var bytesExpectedToExchange = 0
17    
18// Used to track the time taken in transfer, this is for testing purposes.
19// You might get more reliable results using Date to track time
20var transferTimeElapsed = 0.0
21// A new action added to send the image stored in the bundle
22@IBAction func sendImageAsResource(_ sender: Any) 
23{        
24        // Call local function created
25        sendImageAsResource()
26}
27
28func sendImageAsResource()
29{
30        // 1. Get the url of the image in the project bundle.
31        // Change this if your image is hosted in your documents directory
32        // or elsewhere.
33        //
34        // 2. Get all the connected peers. For testing purposes I am only
35        // getting the first peer, you might need to loop through all your
36        // connected peers and send the files individually.
37        guard let imageURL = Bundle.main.url(forResource: "image2",
38                                             withExtension: "jpg"),
39              let guestPeerID = mcSession.connectedPeers.first else {
40            return
41        }
42        
43        // Retrieve the file size of the image
44        if let fileSizeToTransfer = getFileSize(atURL: imageURL)
45        {
46            bytesExpectedToExchange = fileSizeToTransfer
47            
48            // Put the file size in a dictionary
49            let fileTransferMeta = ["fileSize": bytesExpectedToExchange]
50            
51            // Convert the dictionary to a data object in order to send it via
52            // MultiPeer
53            let encoder = JSONEncoder()
54            
55            if let JSONData = try? encoder.encode(fileTransferMeta)
56            {
57                // Send the file size to the guest users
58                try? mcSession.send(JSONData, toPeers: mcSession.connectedPeers,
59                                    with: .reliable)
60            }
61        }
62        
63        // Ideally for best reliability, you will want to develop some logic
64        // for the guest to respond that it has received the file size and then
65        // you should initiate the transfer to that peer only after you receive
66        // this confirmation. For now, I just add a delay so that I am highly
67        // certain the guest has received this data for testing purposes
68        DispatchQueue.main.asyncAfter(deadline: .now() + 1)
69        { [weak self] in
70            self?.initiateFileTransfer(ofImage: imageURL, to: guestPeerID)
71        }
72    }
73    
74func initiateFileTransfer(ofImage imageURL: URL, to guestPeerID: MCPeerID)
75{
76        // Initialize and fire a timer to check the status of the file
77        // transfer every 0.1 second
78        checkProgressTimer = Timer.scheduledTimer(timeInterval: 0.1,
79                                                  target: self,
80                                                  selector: #selector(updateProgressStatus),
81                                                  userInfo: nil,
82                                                  repeats: true)
83        
84        // Call the sendResource function and send the image from the bundle
85        // keeping hold of the returned progress object which we need to keep checking
86        // using the timer
87        fileTransferProgress = mcSession.sendResource(at: imageURL,
88                                          withName: "image2.jpg",
89                                          toPeer: guestPeerID,
90                                          withCompletionHandler: { (error) in
91                                            
92                                            // Handle errors
93                                            if let error = error as NSError?
94                                            {
95                                                print("Error: \(error.userInfo)")
96                                                print("Error: \(error.localizedDescription)")
97                                            }
98                                            
99                                          })
100}
101
102func getFileSize(atURL url: URL) -> Int?
103{
104        let urlResourceValue = try? url.resourceValues(forKeys: [.fileSizeKey])
105        
106        return urlResourceValue?.fileSize
107}
108/// Function fired by the local checkProgressTimer object used to track the progress of the file transfer
109/// Function fired by the local checkProgressTimer object used to track the progress of the file transfer
110@objc
111func updateProgressStatus()
112{
113        // Update the time elapsed. As mentioned earlier, a more reliable approach
114        // might be to compare the time of a Date object from when the
115        // transfer started to the time of a current Date object
116        transferTimeElapsed += 0.1
117        
118        // Verify the progress variable is valid
119        if let progress = fileTransferProgress
120        {
121            // Convert the progress into a percentage
122            let percentCompleted = 100 * progress.fractionCompleted
123            
124            // Calculate the data exchanged sent in MegaBytes
125            let dataExchangedInMB = (Double(bytesExpectedToExchange)
126                                     * progress.fractionCompleted) / 1000000
127            
128            // We have exchanged 'dataExchangedInMB' MB of data in 'transferTimeElapsed'
129            // seconds. So we have to calculate how much data will be exchanged in 1 second
130            // using cross multiplication
131            // For example:
132            // 2 MB in 0.5s
133            //  ?   in  1s
134            // MB/s = (1 x 2) / 0.5 = 4 MB/s
135            let megabytesPerSecond = (1 * dataExchangedInMB) / transferTimeElapsed
136            
137            // Convert dataExchangedInMB into a string rounded to 2 decimal places
138            let dataExchangedInMBString = String(format: "%.2f", dataExchangedInMB)
139            
140            // Convert megabytesPerSecond into a string rounded to 2 decimal places
141            let megabytesPerSecondString = String(format: "%.2f", megabytesPerSecond)
142            
143            // Update the progress an data exchanged on the UI
144            numberLabel.text = "\(percentCompleted.rounded())% - \(dataExchangedInMBString) MB @ \(megabytesPerSecondString) MB/s"
145            
146            // This is mostly useful on the browser side to check if the file transfer
147            // is complete so that we can safely deinit the timer, reset vars and update the UI
148            if percentCompleted >= 100
149            {
150                numberLabel.text = "Transfer complete!"
151                checkProgressTimer?.invalidate()
152                checkProgressTimer = nil
153                transferTimeElapsed = 0.0
154            }
155        }
156}
157func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID)
158{
159        // Check if the guest has received file transfer data
160        if let fileTransferMeta = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Int],
161           let fileSizeToReceive = fileTransferMeta["fileSize"]
162        {
163            // Store the bytes to be received in a variable
164            bytesExpectedToExchange = fileSizeToReceive
165            print("Bytes expected to receive: \(fileSizeToReceive)")
166            return
167        }
168}
169
170func session(_ session: MCSession,
171             didStartReceivingResourceWithName resourceName: String,
172             fromPeer peerID: MCPeerID,
173             with progress: Progress) 
174{
175        
176        // Store the progress object so that we can query it using the timer
177        fileTransferProgress = progress
178        
179        // Launch the main thread
180        DispatchQueue.main.async { [unowned self] in
181            
182            // Fire the timer to check the file transfer progress every 0.1 second
183            self.checkProgressTimer = Timer.scheduledTimer(timeInterval: 0.1,
184                                                           target: self,
185                                                           selector: #selector(updateProgressStatus),
186                                                           userInfo: nil,
187                                                           repeats: true)
188        }
189}
190
191func session(_ session: MCSession,
192             didFinishReceivingResourceWithName resourceName: String,
193             fromPeer peerID: MCPeerID,
194             at localURL: URL?,
195             withError error: Error?) 
196{
197        
198        // Verify that we have a valid url. You should get a url to the file in
199        // the tmp directory
200        if let url = localURL
201        {
202            // Launch the main thread
203            DispatchQueue.main.async { [weak self] in
204                
205                // Call a function to handle download completion
206                self?.handleDownloadCompletion(withImageURL: url)
207            }
208        }
209}
210
211/// Handles the file transfer completion process on the advertiser/client side
212    /// - Parameter url: URL of a file in the documents directory
213    func handleDownloadCompletion(withImageURL url: URL) 
214{ 
215        // Debugging data
216        print("Full URL: \(url.absoluteString)")
217        
218        // Invalidate the timer
219        checkProgressTimer?.invalidate()
220        checkProgressTimer = nil
221        
222        // Set the UIImageView with the downloaded image
223        imageView.image = UIImage(contentsOfFile: url.path)
224}
225

Step 6

Run the code and this is the end result (uploaded to youtube) on the guest side which shows the progress and the file once the transfer is complete and the same progress is shown on the host side as well.

Step 7

I did not implement this but I believe this bit is straight forward:

  1. The file size can be calculated as from the host and it can be sent as a message to the guest on the size to expect

  2. You can can compute the approximate % of a file that has been downloaded by multiplying the progress % by the file size

  3. The speed can be calculated based on the amount of data downloaded / time elapsed so far since the transfer started

I can try to add this code if you feel these calculations are not straightforward.

Update

I have updated the above code samples, github repo and the video to include the final 3 steps as well which gives the final result as follows:

iOS MultiPeer File Transfer with progress, transfer status and transfer speed

Source https://stackoverflow.com/questions/70643079

QUESTION

Memory efficiency of nested functions in python

Asked 2022-Jan-05 at 23:15

Let's say we have the following functions:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11

functionA and functionB will do the same. _outerFunction is globally available, while _innerFunction is only available for functionA. Nested functions are useful for data hiding and privacy, but what about their memory efficiency? For my understanding, the _outerFunction must only be loaded once, while the _innerFunction works like a "local" variable and hence must be loaded each time functionA is called. Is that correct?

ANSWER

Answered 2022-Jan-05 at 23:15

Regarding memory, both of them have almost the same memory footprint.

A function is comprised of a code object, containing the actual compiled code, and a function object containing the closure, the name and other dynamic variables.

The code object is compiled for all functions, inner and outer, before the code is run. It is what resides in the .pyc file.

The difference between an inner and an outer function is the creation of the function object. An outer function will create the function only once, while the inner function will load the same constant code object and create the function every run.

As the code object is equivalent, _inner and _outer's memory footprint is equivalent:

  • In both cases you have the name of the function as a constant. In the functionA the name will be used to construct the inner function object on each run, while in functionB the name will be used to refer to the global module and search for the outer function.
  • In both cases you need to hold a code object, either in the global module or in functionA.
  • In both cases you have the same parameters and same space saved for variables.

Runtime however is not equivalent: functionB needs to call a global function which is slightly slower than an inner function, but functionA needs to create a new function object on each run which is significantly slower.


In order to prove how equivalent they are, let's check the code itself:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11>>> functionA.__code__.co_consts
12(None, <code object _innerFunction at 0x00000296F0B6A600, file "<stdin>", line 2>, 'functionA.<locals>._innerFunction')
13

We can see the code object as a const stored inside functionA. Let's extract the actual compiled bytecode:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11>>> functionA.__code__.co_consts
12(None, <code object _innerFunction at 0x00000296F0B6A600, file "<stdin>", line 2>, 'functionA.<locals>._innerFunction')
13>>> functionA.__code__.co_consts[1].co_code
14b'|\x00|\x01\x17\x00S\x00'
15

Now let's extract the bytecode for the outer function:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11>>> functionA.__code__.co_consts
12(None, <code object _innerFunction at 0x00000296F0B6A600, file "<stdin>", line 2>, 'functionA.<locals>._innerFunction')
13>>> functionA.__code__.co_consts[1].co_code
14b'|\x00|\x01\x17\x00S\x00'
15>>> _outerFunction.__code__.co_code
16b'|\x00|\x01\x17\x00S\x00'
17

It's exactly the same code!

The local variable positions are the same, the code is written the same, and so the actual compiled code is exactly the same.

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11>>> functionA.__code__.co_consts
12(None, <code object _innerFunction at 0x00000296F0B6A600, file "<stdin>", line 2>, 'functionA.<locals>._innerFunction')
13>>> functionA.__code__.co_consts[1].co_code
14b'|\x00|\x01\x17\x00S\x00'
15>>> _outerFunction.__code__.co_code
16b'|\x00|\x01\x17\x00S\x00'
17>>> functionA.__code__.co_names
18()
19>>> functionB.__code__.co_names
20('_outerFunction',)
21

In functionB, instead of saving the name in the consts, the name is saved in a co_names which is later used for calling the global function.

The only difference in memory footprint is thus the code of functionA and functionB:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11>>> functionA.__code__.co_consts
12(None, <code object _innerFunction at 0x00000296F0B6A600, file "<stdin>", line 2>, 'functionA.<locals>._innerFunction')
13>>> functionA.__code__.co_consts[1].co_code
14b'|\x00|\x01\x17\x00S\x00'
15>>> _outerFunction.__code__.co_code
16b'|\x00|\x01\x17\x00S\x00'
17>>> functionA.__code__.co_names
18()
19>>> functionB.__code__.co_names
20('_outerFunction',)
21>>> functionA.__code__.co_code
22b'd\x01d\x02\x84\x00}\x02|\x02|\x00|\x01\x83\x02S\x00'
23>>> functionB.__code__.co_code
24b't\x00|\x00|\x01\x83\x02S\x00'
25

functionA needs to create a function object on each run, and the name for the inner function includes functionA.<locals>., which entails a few extra bytes (which is negligible) and a slower run.


In terms of runtime, if you're calling the inner function multiple times, functionA is slightly faster:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11&gt;&gt;&gt; functionA.__code__.co_consts
12(None, &lt;code object _innerFunction at 0x00000296F0B6A600, file &quot;&lt;stdin&gt;&quot;, line 2&gt;, 'functionA.&lt;locals&gt;._innerFunction')
13&gt;&gt;&gt; functionA.__code__.co_consts[1].co_code
14b'|\x00|\x01\x17\x00S\x00'
15&gt;&gt;&gt; _outerFunction.__code__.co_code
16b'|\x00|\x01\x17\x00S\x00'
17&gt;&gt;&gt; functionA.__code__.co_names
18()
19&gt;&gt;&gt; functionB.__code__.co_names
20('_outerFunction',)
21&gt;&gt;&gt; functionA.__code__.co_code
22b'd\x01d\x02\x84\x00}\x02|\x02|\x00|\x01\x83\x02S\x00'
23&gt;&gt;&gt; functionB.__code__.co_code
24b't\x00|\x00|\x01\x83\x02S\x00'
25def functionA(b, c):
26    def _innerFunction():
27        return b + c
28    for i in range(10_000_000):
29        _innerFunction() # Faster
30
31def _outerFunction(b, c):
32    return b + c
33
34def functionB(b, c):
35    for i in range(10_000_000):
36        _outerFunction(b, c) # Slower
37
38def functionC(b, c):
39    outerFunction = _outerFunction
40    for i in range(10_000_000):
41        outerFunction(b, c) # Almost same as A but still slower.
42
43py -m timeit -s &quot;import temp;&quot; &quot;temp.functionA(1,2)&quot;
441 loop, best of 5: 2.45 sec per loop
45
46py -m timeit -s &quot;import temp;&quot; &quot;temp.functionB(1,2)&quot; 
471 loop, best of 5: 3.21 sec per loop
48
49py -m timeit -s &quot;import temp;&quot; &quot;temp.functionC(1,2)&quot; 
501 loop, best of 5: 2.66 sec per loop
51

If you're calling the outer function multiple times, functionB is significantly faster as you avoid creating the function object:

1def functionA(b, c):
2    def _innerFunction(b, c):
3        return b + c
4    return _innerFunction(b, c)
5
6def _outerFunction(b, c):
7    return b + c
8
9def functionB(b, c):
10    return _outerFunction(b, c)
11&gt;&gt;&gt; functionA.__code__.co_consts
12(None, &lt;code object _innerFunction at 0x00000296F0B6A600, file &quot;&lt;stdin&gt;&quot;, line 2&gt;, 'functionA.&lt;locals&gt;._innerFunction')
13&gt;&gt;&gt; functionA.__code__.co_consts[1].co_code
14b'|\x00|\x01\x17\x00S\x00'
15&gt;&gt;&gt; _outerFunction.__code__.co_code
16b'|\x00|\x01\x17\x00S\x00'
17&gt;&gt;&gt; functionA.__code__.co_names
18()
19&gt;&gt;&gt; functionB.__code__.co_names
20('_outerFunction',)
21&gt;&gt;&gt; functionA.__code__.co_code
22b'd\x01d\x02\x84\x00}\x02|\x02|\x00|\x01\x83\x02S\x00'
23&gt;&gt;&gt; functionB.__code__.co_code
24b't\x00|\x00|\x01\x83\x02S\x00'
25def functionA(b, c):
26    def _innerFunction():
27        return b + c
28    for i in range(10_000_000):
29        _innerFunction() # Faster
30
31def _outerFunction(b, c):
32    return b + c
33
34def functionB(b, c):
35    for i in range(10_000_000):
36        _outerFunction(b, c) # Slower
37
38def functionC(b, c):
39    outerFunction = _outerFunction
40    for i in range(10_000_000):
41        outerFunction(b, c) # Almost same as A but still slower.
42
43py -m timeit -s &quot;import temp;&quot; &quot;temp.functionA(1,2)&quot;
441 loop, best of 5: 2.45 sec per loop
45
46py -m timeit -s &quot;import temp;&quot; &quot;temp.functionB(1,2)&quot; 
471 loop, best of 5: 3.21 sec per loop
48
49py -m timeit -s &quot;import temp;&quot; &quot;temp.functionC(1,2)&quot; 
501 loop, best of 5: 2.66 sec per loop
51def functionA(b, c):  # Significantly slower
52    def _innerFunction():
53        return b + c
54    return _innerFunction()
55
56def _outerFunction(b, c):
57    return b + c
58
59def functionB(b, c):  # Significantly faster
60    return _outerFunction(b, c)
61
62
63py -m timeit -s &quot;import temp;&quot; &quot;for i in range(10_000_000): temp.functionA(1,2)&quot; 
641 loop, best of 5: 9.46 sec per loop
65
66py -m timeit -s &quot;import temp;&quot; &quot;for i in range(10_000_000): temp.functionB(1,2)&quot; 
671 loop, best of 5: 5.48 sec per loop
68

@KellyBundy: What about recursion?

My answer is only true for sequential runs.

If the inner function recurses inside both A and B, there's no real difference in runtime or memory consumption, other than A being slightly faster. If function A and B recurse themselves, B will not allow a deeper recursion but will be significantly faster and will require less memory.

On a sequential run, there is no difference in memory as there is one function object either stored in the global module, or stored as a local variable that is constantly recreated. In case of outside (A | B) recursion there is a memory difference: The local variable where the _innerFunction object is stored is not cleared, meaning there is an additional function object created for every recursion inwards. In this specific example, we can see an important distinction between Python and other languages - Python does not have a tail-call optimization, meaning the frames aren't reused and the variables aren't removed when we recurse inwards, even though no one will reference them anymore.

You're welcome to play with the following visualization.

I guess stack space is exactly identical, all differences are on the heap?

When you're working with Python, it's hard to divide things into a stack and heap. The C-stack is irrelevant as Python uses its own virtual stack. Python's stack is actually linked by the function's currently running frame, and is loaded, or created when a function is invoked. It is the reason they're also called stack frames - there's a linked list or "stack" of frames, and each frame has its own mini-stack called a value-stack. Both the stack and the frame are stored on the heap.

There are plenty of benefits for using this approach, and a nice anecdote would be generator functions. I've actually written an article about the subject, but in short, being able to load and unload the stack at will, allows us to pause a function in the middle of its execution, and is the basis for both generators and asyncio.

Source https://stackoverflow.com/questions/70575617

QUESTION

Stripe-Android SDK doesn’t comply with the User Data and Mobile Unwanted Software policies of Google Play Store

Asked 2021-Dec-11 at 12:03

Yesterday my app was removed from Google Playstore because it was using the Stripe-Android SDK.

Here is the reason why my app was removed from Google Playstore :

We’ve identified that your app is using Stripe SDK or library, which facilitates the transmission and collection of Phone Number and Installed Application information without meeting the prominent disclosure guidelines. Make sure to also post a privacy policy in both the designated field in the Play Developer Console and from within the Play distributed app itself. If necessary, you can consult your SDK provider(s) for further information.

As on my side I do not collect any information of any kind, how could I solve this problem?

ANSWER

Answered 2021-Nov-24 at 08:55

I finally managed to solve the problem. What you have to do is:

  1. Update the Stripe SDK
  2. Upload your app update to all release tracks (production, open, closed and internal), incrementing the version number each time. Right after uploading the APK file and before resubmitting your app for review, please make sure to deactivate the non-compliant APK (*).
  3. Go to the Publishing overview page and click Send for review to submit your changes. (This is important. I had missed this point)

Maybe Google will ask you to add a privacy policy too. You will have to:

  1. Post a privacy policy explaining very precisely how you collect data and what you do with it. (even if you do not collect any data)

  2. On the play console page, go to App Content -> Privacy policy and enter the URL of your privacy policy.

  3. Inside your app, put a link to your privacy policy. (I missed that point too)

Less than 24 hours later, my app became accessible again on Play Store.

Good to know 1 : If you've done all of these steps and your app is still offline, you can contact the Google policy support team at https://support.google.com/googleplay/android-developer/contact/emailappeals

(*) Good to know 2 : Here is where you can deactivate the non-compliant Bundle: enter image description here

Source https://stackoverflow.com/questions/69072021

Community Discussions contain sources that include Stack Exchange Network

Tutorials and Learning Resources in Privacy

Tutorials and Learning Resources are not available at this moment for Privacy

Share this Page

share link

Get latest updates on Privacy