__init__.pyがないディレクトリに出るエラー

基本的なことなんだけど、この手のエラーに関する日本語の情報が少なかったので書き残し。

pythonディレクトリを作るときは __init__.py が必須。当たり前の話だけど初心者なのでちょっとハマった。
__init__.py がなくても動く場合があるからややこしいんだけど)
ない状態だと、同じ構成の名前空間が同一パッケージ内にあると衝突する。

例えば、

root_dir
├ package_name_A
│ ├ views.py
│ └ tests.py
└ package_name_B
  ├ views.py
  └ tests.py

root_dir
├ package_name_A
│ ├ views.py
│ └ tests
│   └ tests.py
└ package_name_B
  ├ views.py
  └ tests
    └ tests.py

にしたいとき、それぞれの tests/__init__.py を置かないと、

____________ ERROR collecting root_dir/package_name_A/tests/tests.py ____________
import file mismatch:
imported module 'tests' has this __file__ attribute:
  C:\Users\user\Desktop\dir\root_dir\package_name_B\tests
which is not the same as the test file we want to collect:
  C:\Users\user\Desktop\dir\root_dir\package_name_A\tests\tests.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
 generated xml file: C:\Users\user\Desktop\dir\junit.xml

になる。
tests/tests.py が2つあるので衝突する。
もしこれが

root_dir
├ package_name_A
│ ├ views.py
│ └ tests.py
└ package_name_B
  ├ views.py
  └ tests
    └ tests.py

だったら問題なく動く。
ちょっと不思議。

GitHubにDraft Pull Requestが追加されたけど利用制限があるみたい

2/15ごろからGitHubにDraft pull requestという機能が追加されました。
WIPを付けたPRを立てなくてもいい!と期待の機能でしたが、
いざ使おうと思ったらそんなボタンがどこにも存在しない。
よく見ると、全リポジトリに対して開かれた機能じゃなかったようです。

Pull Requestのヘルプページ

https://help.github.com/articles/about-pull-requests/#draft-pull-requests

によると、

Draft pull requests are available in public repositories with GitHub Free and GitHub Pro, and in public and private repositories with GitHub Team, GitHub Enterprise Cloud, and GitHub Enterprise Server.

とのこと。
日本語でまとめると、利用可否は下記のようになっていると書いてあります。

パブリックで対応, プライベートで非対応

パブリックもプライベートも対応

GitHub Proかつプライベートリポジトリでもダメとは。悲しみ…
自分の業務では恩恵に預かれませんでした。
対象リポジトリの拡張を待ちます。頑張ってGitHub先生…

MacOS 10.7 Lion でHTTPのページが開けない&設定からAppleIDを登録できない現象

スタートの状態

会社の先輩ら古めのMacBookを譲り受けた。
初期化済み&初期化後の確認のため、適当なrootユーザー作成済み。
osはMacOS 10.7 Lion。

発生したこと

  • HTTPSのページが開けない
  • 「設定」からAppleIDサインインできない(アプリDLできない、iCloud繋げない)
  • AppleIDサインインできないのでAppStoreからAppダウンロードができない
  • MacOSはAppStoreからダウンロードする必要がある
  • バージョンアップできない
  • Appleの公式サポートに、SSL通信の問題が起きたらバージョンアップしろと書いてあった
  • 詰み

解決方法

やったこと

1. 再初期化
2. ようこそ画面でID登録
3. AppStoreからEl Capitanインストール
4. Mojaveをインストール

※一度El Capitanを挟んだのは、10.7から直接Mojaveにバージョンアップできないため。
Mojaveに直接アップデートできるのは10.8以降。

変わること

  • 設定からどう頑張ってもAppleIDにサインインできなかったが、ようこそ画面からだと一発でサインインできた
  • →AppStoreからアプリを落とせるように→El Capitan、Mojaveがダウンロードできた
  • →バージョンアップしたらHTTPSにつながるようになった

原因

Apple公式でのQAを見るに、10.8.5以前のMacOSSSL周りが死んでるらしい。おそらくこれが原因
https://discussionsjapan.apple.com/thread/110210266?answerId=101111151122#101111151122

とりあえずMacは困ったら初期化

Node.js + Jestで「SecurityError:~」が発生した際の解決法

こんなエラーが出た

> jest

 FAIL  test\fileA.test.js
  ● Test suite failed to run

    SecurityError: localStorage is not available for opaque origins
      
      at Window.get localStorage [as localStorage] (node_modules/jsdom/lib/jsdom/browser/Window.js:257:15)
          at Array.forEach (<anonymous>)

突然出たエラー。これまでと特に設定は変えてない。なぜ?(。´・ω・)

見つけたページ

https://qiita.com/supaiku2452/items/972eb7e03414c695d033

でも` jest.config.js `というファイルはない。どこに書けば良いかちょっと迷った。

最終的な解決方法

https://github.com/facebook/jest/issues/6769
https://doc.ebichu.cc/jest/docs/ja/configuration.html

package.jsonに書けばいいらしい。
既に存在するルートディレクトリのpackage.jsonに "jest" を追加する。

{
  "name": "my_application_name",
  "version": "1.0.0",
  "description": "my application description",
  "jest": {
    "verbose": true,
    "testURL": "http://localhost/"
  },
  "main": "index.js",
  
  ...

適当なところに追記しました。
これでJestを再実行するとテストが通った。

解決!

Puppeteer入りNode.jsアプリをServerlessでAWS Lambdaにデプロイするときに困ったこと

結論

nodejs+puppeteer+lambdaのアプリケーションには .npmrc が必須

起きたこと

C:\[hogehoge]>npx sls deploy

をしたら

  Serverless Error ---------------------------------------

  An error occurred: [Function Name] - Unzipped size must be smaller than 262144000 bytes (Service: AWSLambda; Status Code: 400; Error Code: InvalidParameterValueException; Request ID: [fugafuga]).

と怒られました。
Serverlessのログを見てみると、

~略~
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (157.42 MB)...
Serverless: Validating template...
~略~

157MBのzipをアップロードしようとしている。
Lambdaの容量制限は50MBなのでそりゃ怒られる…

調査したこと

ディレクトリのサイズを調査すると、node-module で300MBくらい食ってることが判明。
何度か npm install を繰り返して原因究明。
アプリケーションに使用していたPuppeteerの中にChromium(130MBくらい)が含まれていた。こいつのせい!
こいつがデプロイの時に含まれないようにしたいと思い、除外設定を探すもヒットせず。

解決法

たどり着いた神記事がこちら
https://www.pressmantech.com/tech/serverless/4613

ちょっと自分の対応の角度はズレてた。
解決法は「デプロイに含めない」ではなく、「そもそもインストールしない」だった。

というわけで、↑の記事に従って以下の手順を踏んだ。


1. node-moduleディレクトリを全削除

2. .npmrcをルートディレクトリに作る(書くのは1行だけ)

PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=TRUE

3. npm install

これで skip install が出た。
もう一回デプロイ挑戦したらzipのサイズは130MBくらい減っていて、無事lambdaにアップロードされていった。
ありがたや。

Djangoで空文字・Noneを許容する文字列Validateを作る

やりたいこと

DjangoのSerializerで、
空文字とNoneと、ついでに未入力も許容する文字列バリデータをつくる

結論

CharFieldを定義するときに、
requiredだけでなく allow_blank, allow_null も設定しよう(でも罠がある)

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False, allow_blank=True, allow_null=True)
>>> serializer = TestSerializer(data={})
>>> serializer.is_valid()
True
>>> serializer2 = TestSerializer(data={'hoge': ''})
>>> serializer.is_valid()
True
>>> serializer3 = TestSerializer(data={'hoge': None})
>>> serializer.is_valid()
True

やってみること

テストのためにシリアライザを用意しました。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField()

ここから ""(空文字)、None、そもそも値がない の3パターンが通るようにしていきます。

今の状態でバリデーションをかけてみる
>>> from fuga.serializers import TestSerializer
>>> serializer = TestSerializer(data={})
>>> serializer.is_valid()
False
>>> serializer = TestSerializer(data={'hoge': ''})
>>> serializer.is_valid()
False
>>> serializer = TestSerializer(data={'hoge': None})
>>> serializer.is_valid()
False

すべてFalseになります。

試したこと

CharFieldには状態の異なる「空」を許容するオプションが3つあります。
それらを全て許容状態にしていきます。

1. required

required はすべてのフィールドクラスに共通するバリデートで、
”引数がシリアライザに渡されたかどうか”だけを見ます。
どんな値が入っているかはチェックしません。
デフォルトはTrue。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False)
>>> serializer = TestSerializer(data={})
>>> serializer.is_valid()
True

これでまず、そもそも値が存在しない場合がクリア。

2 allow_blank

allow_blank は空文字とNoneを許容するバリデートで、デフォルトはFalseです。
CharFieldでは allow_null が非推奨で、こちらが推奨されています。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(allow_blank=True)
>>> serializer = TestSerializer(data={'hoge': ''})
>>> serializer.is_valid()
True

>>> serializer2 = TestSerializer(data={'hoge': None})
>>> serializer.is_valid()
True
3. allow_null

allow_null はすべてのフィールドクラスに実装されているバリデートです。
nullを許容するか否かだが、CharFieldでは空文字かnullかの判定になります。
デフォルトはFalse。
上で述べたように、CharFieldでは非推奨とされているようです。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(allow_null=True)
serializer = TestSerializer(data={'hoge': None})
serializer.is_valid()
True

serializer2 = TestSerializer(data={'hoge': ''})
serializer.is_valid()
True

注意点

allow_blankとallow_nullは上で書いたように、
一つずつオプション指定をすると、
空文字とnullの両方を許容する挙動でした。
しかししかし、required=False と組み合わせると、
allow_blankはnullを、allow_nullは空文字を許容しなくなりました。
どういうことなの……

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False, allow_null=True)
s = TestSerializer(data={'hoge': ''})
s.is_valid()
False
s2 = TestSerializer(data={'hoge': None})
s2.is_valid()
True
class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False, allow_blank=True)
s = TestSerializer(data={'hoge': ''})
s.is_valid()
True
s2 = TestSerializer(data={'hoge': None})
s2.is_valid()
False


Serializer fields - Django REST framework
では、

It is valid to set both allow_blank=True and allow_null=True, but doing so means that there will be two differing types of empty value permissible for string representations

とあるため、2つセットしたときの挙動のほうが正しそう…?な気がします。
ただ、2つ使う時はバグに気をつけろ的記述もあるため注意したいところ。

上記の挙動を踏まえて、とりあえず今回はallow_blankもallow_nullもセットしましたが、
厳密なチェックを行う場合は注意してください。

感想

required=Falseにすれば、値がないのも空文字もNullも等価な世界線から来たので、
required=Falseにしてるのにバリデートが通らなくて結構苦戦した…
Django触り始めて1か月くらい、知れば知るほどDjangoさんは奥が深い。

しかしなぜオプションの数で挙動が変わるの…?今度調査したい。します。がんばる

香港旅Tips(MTR、女人街、香港ディズニーランド)

目標:一般のかたにも有益なブログになる
 → ひねり出したネタ:海外旅行Tips

(^o^)
これは2018年5月に初めて香港に行った自分の教訓や気付きですので、 すべての方に有益というわけではございません。
海外旅行は事前の下調べが大変重要になります。 他の情報と合わせて吟味してください。 少しでも誰かの役に立てば幸いです。

海外旅行Tips香港編

TL;DR

空港からMTRで街へ出るときのTips

  1. MTRの路線

  2. 空港から出ているMTR路線は”Airport Express”のみ。

  3. Airport Expressの香港行きは停車駅が
    青衣(ツィンイー)、九龍(カオルーン)、香港(ホンコン)
    のみのため、
    空港から一直線に香港ディズニーランドを目指すと、一度青衣まで出る必要がある。
  4. 青衣をすぎると次は九龍なのだが、九龍から旺角など市街地へは2~30分歩くことになる。
    移動が結構難しいので、MTRの路線図は事前に調べておくとよいです。
    Google Mapの経路案内もとても役に立つ!)

  5. MTRのチケット

Airport Expressのチケットは、空港のMTR有人カウンターで買います。
3人以上でチケットを購入すると割引あり。
個別会計にすると割引は適用されません。
友人同士で行くときなどは、個別会計式にせずまとめて購入することをおすすめ。

また、Airport Expressは特別な路線のため、空港ではAirport Expressの駅までのチケットしか買えません。
(つまり、青衣、九龍、香港行きのいずれかになる。)
他の目的地へ行きたい場合は、Airport Expressを降りてから購入することになります。

  1. MTR車内について

コンセントあり。Wifiあり。自由席。
乗車時間は九龍までで約30分。はやいよ!

女人街とか旺角の歩き方

  1. 女人街 買わせる圧がすごい。押しの弱い人は気がついたら買うことになってる。(自分がそうだった)

買わされないために

  • 「説明ありがとう」の意味で「Thank you」は絶対にだめ →「OK!買います!」の意味に取られる →基本は「No」しか言うな

  • 値引き交渉をしろ

    • 頑張ると半額くらいまで下がる
    • つまり…本来の価格は……?
  • 中国っぽいもの以外は極力買うな

    • あなたがいいなと思った洒落た製品はだいたいコピー品です
    • 関税で没収されるよ

とはいえ見てるだけで楽しいのでぜひとも一度行ってみよう

  1. 旺角

  2. 女人街の近く

  3. 渋谷・原宿を圧縮して全体的に高さを加えたような街並み
  4. ブランドショップもあるので、正規品が買えるよ
  5. デパートも多いのでブランド物探しに行くといいかもしれない
  6. 注意:正規品でも日本に持ち込めない海外ブランドがある 関税で没収されることになるので知らないブランドの衝動買いは避ける(SuperDryとか)

香港ディズニーランドTips

  1. 交通
  2. MTRでSunny Bay駅を降りて、Disneyland Resort Lineに乗り換える
  3. 降りたら人の流れに流されてディズニーっぽい道を進むとある
  4. 簡単!

  5. チケット

  6. 事前にオンラインで購入しておくこと推奨
  7. インターネットから事前に購入すると割引あり、フードの無料クーポンなどもつく
  8. ミールバウチャーもおすすめ。

    • 指定レストランでのお食事券。2食or3食分+ワッフルなど軽食がセットになって割引価格で買える。
    • 3 in 1のタイプを購入して高いものと交換していくと、普通購入に比べて約100HKD(1500円くらい)安くなる計算
  9. アトラクション

  10. だいたい5分待ち。トイストーリーエリアだけちょっと長くて30分とか。
  11. アトラクションよりグリーティングのほうが混雑する。
    • グリーティングは18時くらいで終わってしまうのも理由の一つ
    • 特にアイアンマン、ダッフィーは人気で、4~50分待ち。そして一日を通して混雑状況はほとんど変わらない

以上!

Todo: - 思い出したことあったら書き足す - 時間があったら写真もupする - 途中からリスト形式になってるのをいずれなおす