codingecho

日々の体験などを書いてます

読書と行動することのROI(Return on Investment)は高い

数年前に「ソフトスキル」という本を買って読んでブログを書くようにしました。

2000円ぐらいで買ったと思います。ブログの内容は日々業務で経験したソフトウェア開発の話が主でした。

1年ぐらい続けた後にブログを見てくれた、とある企業からオファーをもらい転職することにしました。その時、投資した2000円+行動に対して数百倍収入が増えました。

この本に出会わない、または行動しなければ転職することもなかったでしょう。

読書をしてそこから得られたことを行動としてアウトプットしてみましょう。

「とっしん本 #1」を技術書典6に出展します

「とっしん本 #1」を技術書典6に出展します。技術書典6にお越しの際はぜひお立ち寄りください!

配置は「 こ31」です。

f:id:ytanak:20190322080548p:plain
「とっしん本 #1」の表表紙

内容について

ページ数: 約100ページ

この本では、勉強会である「とっしんの会」のメンバー3人がそれぞれが興味のある内容を本にしました。

以下について入門者向けの内容が紹介されています。

難しくはないので、興味のある内容について誰でも楽しんでもらえると思います!

技術書典6 開催情報

日時: 2019年4月14日 (日) 11:00 - 17:00 場所: 池袋サンシャインシティ2F 展示ホールD (文化会館ビル2F)

techbookfest.org

サークルWebページ: とっしんの会

Cloud Deployment Managerでバケット作成時のパーミッションエラーはバケット名が原因かもしれない

Cloud Deployment Managerでプロビジョニングするとき、エラー表記で少しハマったところについてメモ。

Cloud Storageをプロビジョニングするとき以下のようなエラーが出ることがあります。

$ gcloud deployment-manager deployments create example --config vm.yaml
The fingerprint of the deployment is YjuHda-HR8_aaaaaaaaaaa==
Waiting for create [operation-1549936918163-581a8cf6e71ac-aaaaaaaa-3f20fe82]...failed.                                                                                      
ERROR: (gcloud.deployment-manager.deployments.create) Error in Operation [operation-1549936918163-581a8cf6e71ac-aaaaaaaa-3f20fe82]: errors:
- code: RESOURCE_ERROR
  location: /deployments/batch/resources/example
  message: '{"ResourceType":"gcp-types/cloudfunctions-v1:projects.locations.functions","ResourceErrorCode":"500","ResourceErrorMessage":"Failed
    to retrieve function source code"}'
- code: RESOURCE_ERROR
  location: /deployments/batch/resources/image
  message: '{"ResourceType":"gcp-types/storage-v1:buckets","ResourceErrorCode":"403","ResourceErrorMessage":{"code":403,"errors":[{"domain":"global","message":"example@cloudservices.gserviceaccount.com
    does not have storage.buckets.get access to image.","reason":"forbidden"}],"message":"example@cloudservices.gserviceaccount.com
    does not have storage.buckets.get access to image.","statusMessage":"Forbidden","requestPath":"https://www.googleapis.com/storage/v1/b/image","httpMethod":"GET","suggestion":"Consider
    granting permissions to example@cloudservices.gserviceaccount.com"}}'

does not have storage.buckets.get access to image.","statusMessage":"Forbidden"Consider granting permissions to example@cloudservices.gserviceaccount.com と言われるので、普通に考えるとパーミッションかなと思いサービスアカウントの権限を確認しますが十分な権限が付与されていました。

結論としては、作成しようとしているバケットの名前が他のユーザーのバケット名と被っているためです。バケットの名前は全てのユーザーでユニークな名前にする必要があります。

当たり前ですが作成しようとしているバケットが自分のものではない場合、そのバケットにアクセスする権限はありません。なので、does not have storage.buckets.get access to image のような表記になっています。

本のカバーはなぜ必要なのか?

カバーといっても書店の店員さんが後からつけてくれるやつではなくて、初めから付いている値段やバーコードが書かれているカバーです。

本を読むときに邪魔になっていたのでなぜ必要なのか以前から気になっていました。

「本を汚れから守る」ようなことを見かけましたが、出典元を探せていませんでした。多分それっぽいとは思っていましたが。

それについて、先日たまたま読んだ「出版」に記載がありました。

p.52

カバーの存在理由。それはひとつに、「取り替えられる」からである。

出版〈2018年度版〉 (産業と会社研究シリーズ)

出版〈2018年度版〉 (産業と会社研究シリーズ)

間違いなさそうです。

取り替えのサイクルとして

  1. 本が手に取られて汚れる
  2. 取次に返品
  3. 取次でカバーを変えて書店に戻る

の流れがあるようです。カバーがあるおかげで、汚れた本を処分することなく新品として書店に戻るわけです。

また、取次に戻らずに出版の営業の人が書店でカバーだけを変えていくパターンもあるようです。

ちなみにカバーも編集の人に依頼された(出版に所属するか外部の)デザイナーさんが考えて作ってくれたものなので、捨てるときはその辺を思い出してみるといいかもしれません!

PythonでgRPCのリクエストをする

PythonでgRPCを扱う方法です。クライアント側だけの紹介です。

Protocol Buffersの定義ファイルを用意します。

crawl.proto

syntax = 'proto3';

package proto;

service Crawler {
    rpc Crawl(CrawlRequest) returns (CrawlResponse);
}

message CrawlRequest {
    string url = 1;
}

message CrawlResponse {
    string content = 2;
}

Protocol Buffersの定義ファイルを、Pythonのソースファイルにコンパイルします。

以下のコマンドを実行します。

$ protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/crawl.proto

$SRC_DIR: protoファイルがあるディレクトリ

$DST_DIR: コンパイルしたソースファイルの保存先

crawl_pb2.pycrawl_pb2_grpc.py のファイルが生成されます。

生成されたPythonのファイルを利用してgRPCのサーバーに対してリクエストします。

import grpc
from crawl_pb2_grpc import CrawlerStub
from import CrawlRequest

def crawl(url):
    channel = grpc.insecure_channel('localhost:50005')
    crawl_request = CrawlRequest(url=url)  # リクエストで送るデータの生成
    stub = CrawlerStub(channel)  
    result = stub.Crawl(crawl_request)  # リクエスト

    print(result.content)  # レスポンスのcontentを取得

参考

Pythonで文字列に絵文字と半角カタカナが含まれているか判定する

ここではPython 3.7を使った方法を紹介します。

絵文字の判定

現時点の最新版であるUnicode® Emoji Charts v11.0では、絵文字のUnicodeは1F600から1F3F4の範囲でコードが振られています。

なので、Unicodeの範囲を指定すればいけるじゃんと思っていたのですが、絵文字についてはUnicodeが連番ではなかったので正規表現で範囲を指定した方法ができませんでした。WikipediaのUnicode表を参考に範囲を指定していますが、絵文字以外も判定されてしまいます。

以下がダメなサンプルコードです。

text = '今日はいい天気'
if re.search('[\U00010000-\U0010ffff]|[\U000000A9-\U00003299]', text):
    print('found it!')
else:
    print('not found')

ちなみに、新しい絵文字を扱うため \U (UTF-32) を使っています。

上記のサンプルコードの範囲の間にはひらがななどの絵文字ではない文字が含まれているのでこのコードでは絵文字を判定できません。

いろいろ調べてみて最終的にemojiというライブラリが良さそうなのでこちらを使用しました。

import emoji

def text_is_emoji(text):
    for c in text:
        if c in emoji.UNICODE_EMOJI:
            return True
    return False

text = '今日はいい天気'

if text_is_emoji(text):
    print('found it!')
else:
    print('not found')

結果

not found
import emoji

def text_is_emoji(text):
    for c in text:
        if c in emoji.UNICODE_EMOJI:
            return True
    return False

text = '今日はいい天気☀️'

if text_is_emoji(text):
    print('found it!')
else:
    print('not found')

結果

found it!

半角カタカナの判定

半角カタカナのUnicodeはFF61からFF9Fまでの範囲が決まっていので簡単です。

以下は半角カタカナを判定するサンプルコードです。UnicodeのエンコーディングにはUTF-8を使います。

全角カタカナ

import re

text = '今日はイイ天気'
if re.search('[\uFF61-\uFF9F]', text):
    print('found it!')
else:
    print('not found')

結果

not found

半角カタカナ

import re

text = '今日はイイ天気'
if re.search('[\uFF61-\uFF9F]', text):
    print('found it!')
else:
    print('not found')

結果

found it!

正規表現で UTF-8 と UTF-32 の扱いかた

Pythonの正規表現でUnicodeを扱うにはコードの前に \u または \U を使用します。

UTF-8の場合は \u、UTF-32の場合は \U を使います。詳しくは公式ドキュメントを参照してください。

おわりに

絵文字に関してはUnicode® Emoji Charts v11.0のようにバージョンが振られていることからもわかるように、絵文字の数が年々増えています。なので、絵文字が正しく判定されているか適宜見直していく必要がありそうです。

参考