くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

プログラム

Python matplotlibのインストールでグダる

Pythonでグラフ表示したくなったので、matplotlibをインストールしようとしたら

かなりグダったので残しておく。

matplotlibの公式

OSはCentOS7

pip install matplotlib

↑で基本は良いようですが、これだけだとCentOSでは動かない。

GUI系の処理をするのにtcl/tkを使ってるらしく、

それのPython用モジュールが標準だとPython3側にはないっぽ。

なのでPython3用のやつをインストールする。

sudo yum install tcl -y
sudo yum install tcl-devel -y
sudo yum install tk -y
sudo yum install tk-devel -y
sudo yum install tkinter -y
sudo yum install python-matplotlib-tk -y

なんだけど、これでもtkinterが入らない。

探してみると

> yum search tkinter

python3-tkinter.i686 : A GUI toolkit for Python
python3-tkinter.x86_64 : A GUI toolkit for Python
python34-tkinter.x86_64 : A GUI toolkit for Python 3
python35u-tkinter.x86_64 : A GUI toolkit for Python 3
python36-tkinter.x86_64 : A GUI toolkit for Python
python36u-tkinter.x86_64 : A GUI toolkit for Python

なので、

sudo yum install python36u-tkinter -y

でやってみると、下の感じでモジュールが競合してんぞって怒られる。

・・・
file /usr/lib64/python3.6/xml/sax/__pycache__/xmlreader.cpython-36.opt-2.pyc from install of python36-libs-3.6.6-5.el7.x86_64 conflicts with file from package python36u-libs-3.6.7-1.ius.centos7.x86_64
・・・

で、これを解消するには一回Python3をアンインストールする必要があるっぽい。

コワ。

なので

sudo yum remove python36u

sudo yum install python36u
sudo yum install python36u-devel

↑でアンインスト+インストしなおし。

そしたら、↓でOK。

sudo yum install python36u-tkinter -y

長かった。

あと、途中でAggはもう使えないよってエラーが出るので、

matplotlib.use('TkAgg')

を入れてみたり、↓の設定ファイルを書き換えたりする必要がある

vi ~/pyenv/lib/python3.6/site-packages/matplotlib/mpl-data/matplotlibrc

## 'module://my_backend'.
backend      : Tkagg

って事をみたのですが、インストールが上手くいった後だと、

特にやらなくてもちゃんと動いた。なんでしょね。

Python Djangoで動的にモデルを作成

Djangoのモデルはすごく便利なのですが、

固定の定義を作らないとダメなので、

何かの設定とかに応じてモデルを作りたい場合はちょっと使えない。

SQLを自前で書いてゴニョゴニョしてもいいんだけど、

ORMは出来ればDjangoの使いたいのでどうにかしたい。

ということで、モデルを動的に作りたくなった場合。

あと、動的に作ったモデルのテーブルも動的に作りたい場合についても。

参考:https://code.djangoproject.com/wiki/DynamicModels

※すごく古いのでちょっと微妙かも。。。

まず、動的にモデル作る本体は下の感じ。

from django.apps import apps
from django.db import models

def create_model_class(cls, name: str, fields=None, app_label: str = "", module: str = "", options=None):
    """指定されたモデルのClassを作成する"""
    class Meta(object):
        pass
    if app_label:
        setattr(Meta, "app_label", app_label)
    if options is not None:
        for key, value in options.items():
            setattr(Meta, key, value)
    attrs = {"__module__": module, "Meta": Meta}
    if fields:
        attrs.update(fields)
    model = type(name, (models.Model,), attrs)
    return model

普通にクラス定義するときにやってる事を、コードで書いてるだけ。肝は↓のとこ。

type(name, (models.Model,), attrs)

models.Modelを継承したクラス定義にする。

で、どう使うかというと↓の感じ。

app_label = "app"
model_name = "DynamicModelName"
ops = {"db_table": "table_name"}
fields = {}
fields["id"] = models.AutoField(primary_key=True, null=False)
fields["test01"] = models.IntegerField(null=False, default=0)
fields["test02"] = models.CharField(null=False, max_length=255, default="")
ret = create_model_class(
    name=model_name,
    app_label=app_label,
    module=".".join([app_label, model_name]),
    options=ops,
    fields=fields
)

obj = ret()
obj.test01 = 123
obj.test02 = "abc"
obj.save()

q = ret.objects.all()
q.values()

ただ、動的に作ったモデルはDBにはまだテーブル作られてないので、

save()とか、objects.all()とかはもちろん動ない。

テーブルをこの作ったモデル定義が作成する場合はschema-editorを使う。

こんな感じ。

from django.db import connections

connection = connections[con_label]
with connection.schema_editor(collect_sql=False, atomic=True) as se:
    se.create_model(model)  # ここでCreate Tableが発行される

SQLだけ拾いたいときは下の感じ

with connection.schema_editor(collect_sql=True, atomic=True) as se:
    se.create_model(model)
    "\n".join(se.collected_sql)

collect_sqlをTrueにすると、操作に対してのSQLがcollected_sqlのlistに

たまってくので、最後に「"\n".join(se.collected_sql)」で全部取るみたいな。

collect_sqlをTrueにした場合は、create_model()とかしても、SQLは発行されない。

あと、作ったモデルは↓の感じで使いまわせる。

from django.apps import apps

django_model_name = model_name.lower()
if django_model_name in apps.all_models[app_label].keys():
    ret = apps.all_models[app_label][django_model_name]

q = ret.objects.all()
q.values()

「apps.all_models」がDjangoがmodel定義を全部キャッシュしてある場所。

削除したい場合はここから消せばいい気がするんだけど、

ちゃんと調べてないので、要調査。今のとこ使い終わった場合は↓の感じで消す。

def unload(cls, app_label, model_name):
    django_model_name = model_name.lower()
    if app_label in apps.all_models:
        mdic = apps.all_models[app_label]
        if django_model_name in mdic.keys():
            mdic.pop(django_model_name)
            apps.clear_cache() # これないとapps.get_models()から消えてない

あと、外部キー制約がどうもうまくいかなかった。

他にも上手くいかないオプションはあると思うのですが、

単純なテーブルのORMやるだけなら十分。。。

と、ここまで書いてて、こんなパッケージがある事に気づいた。

後でコード見ておこ。。。

C# HttpClientでApacheにPOST送ったら502

すごいはまった。

こんな感じで、JSONデータをPOSTするリクエスト。

string url = @"http://localhost/****";
using (var client = new HttpClient())
{
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Content = new StringContent(@"{***}", Encoding.UTF8, "application/json");
    var response = await client.SendAsync(request);
    var restext = await response.Content.ReadAsStringAsync();
    Console.WriteLine(restext);
}

そしたら、結果が↓になる。サーバはApache。

502 Proxy Error

リバースプロキシでバックエンドに飛ばしてるんですが、

Apacheのログをdebugレベルにしてログを見ると、

# access.log

"POST /hogeurl HTTP/1.1" 502 341 "- - - - - 202 Keep-Alive"

# error.log

[proxy_http:error] [pid 14412:tid 1408] (20014)Internal error (specific information not available): [client ::1:50107] AH01102: error reading status line from remote server 127.0.0.1:3002
[proxy_http:debug] [pid 14412:tid 1408] mod_proxy_http.c(1311): [client ::1:50107] AH01105: NOT Closing connection to client although reading from backend server 127.0.0.1:3002 failed.
[proxy:error] [pid 14412:tid 1408] [client ::1:50107] AH00898: Error reading from remote server returned by /hogeurl

みたいな感じ。何もわからん。

アプリ側のログには何も書かれてないのでそもそも、アプリまで到達してないっぽい。

普通のGetはちゃんと行くし、PostManで同じPOST投げてもちゃんと上手くいく。

という事は、きっと変なヘッダがついてるんだろうと。

で、C#って発行するリクエストの最終的なHeaderってどうやって見ればいいんですかね?

Apache側でログの指定ってヘッダのキー指定で一つ一つ見るしかなさそうなので、

変なヘッダ入ってないか確認するのが出来ないのですが・・・。

良いやり方ないもんか。。。

まーいいや。結論から言うと、C#のHTTPClientで発行するPOSTにはデフォで↓がつくっぽい。

Expect: 100-Continue

拡張ヘッダという事みたいですが、これが悪さしてるっぽい。というかApacheと相性悪い?

これを送らないようにするために、↓にする。

string url = @"http://localhost/****";
using (var client = new HttpClient())
{
    ServicePointManager.Expect100Continue = false;//これ!!
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Content = new StringContent(@"{***}", Encoding.UTF8, "application/json");
    var response = await client.SendAsync(request);
    var restext = await response.Content.ReadAsStringAsync();
    Console.WriteLine(restext);
}

もーね・・・。って感じ。

以下、上に行きつくまでに試した事。

まず、プロキシがおかしいのかとApacheでプロキシまわりのエラーが起きたときに

とりあえずやってみる設定は↓。

# httpd.conf

SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1

そしたら、↓を返すようになった。

417 Expectation Failed

そのほか、POSTManで指定してるヘッダ入れてみたり色々してみて、

この417から「Expect: 100-Continue」に行き着いた感じ。

何かこー、アレな感じですね。。。

バースデーパラドックス

バースデーパラドックス(birthday paradox)っていうのがある。

人が集まった時に誕生日(月日の部分)が同じ人がいる確率はどんなもんかってやつ。

20人くらい集まると意外にも「40%」くらい。

23人になると、「50%」くらいになる。

結構確率高くない?

直観的な感覚と反するのでバースデーパラドックスっていうみたい。

母数がnの場合に、1.18×√nになると50%超えるらしい。

誕生日だと1年365日として1.18×√365(19.1)≒22.53なので23人くらいで

同じ誕生日の人が1組はいるって確率は50%って感じ。

※「自分と」同じ誕生日の人がいる確率じゃなくて集団の中で同じ誕生日の組が存在する確率。

で、実際にやってみた。以下Pythonで書いたコード。

365日で決まった試行回数ランダムに数値を引いて、かぶったのがあったかどうかを

10000回やってみて、どのくらいの割合かを見た感じ。

それを試行回数19、22、23回でそれぞれ5回ずつ。

import random

def challenge(limit):
    tmp = []
    for i in range(limit):
        x = random.randint(1, 365)
        if(x in tmp):
            return True
        tmp.append(x)
    return False

def birthday(n):
    ret = []
    for i in range(10000):
        if(challenge(n)):
            ret.append(1)
        else:
            ret.append(0)
    return ret

if __name__ == "__main__":
    print("n=19")
    for i in range(5):
        ret = birthday(19)
        print(f"{ret.count(1)} / {len(ret)} = {ret.count(1) / len(ret)}")

    print("n=22")
    for i in range(5):
        ret = birthday(22)
        print(f"{ret.count(1)} / {len(ret)} = {ret.count(1) / len(ret)}")
    
    print("n=23")
    for i in range(5):
        ret = birthday(23)
        print(f"{ret.count(1)} / {len(ret)} = {ret.count(1) / len(ret)}")

結果

n=19
3814 / 10000 = 0.3814
3693 / 10000 = 0.3693
3807 / 10000 = 0.3807
3785 / 10000 = 0.3785
3724 / 10000 = 0.3724

n=22
4846 / 10000 = 0.4846
4734 / 10000 = 0.4734
4752 / 10000 = 0.4752
4708 / 10000 = 0.4708
4759 / 10000 = 0.4759

n=23
5047 / 10000 = 0.5047
5058 / 10000 = 0.5058
5092 / 10000 = 0.5092
5007 / 10000 = 0.5007
5079 / 10000 = 0.5079

おお、確かにそんな感じになってる。。。

365程度だと、23回試行すると一回くらいは重複する可能性が50%あると。

100000くらいでも、373回試行で、1回くらい重複する可能性が50%。

割とかぶる可能性高いよね。

そう考えると、小学校のクラスって自分の時は30~40人くらいだったんですが、

割と同じ誕生日の人がいてもよさそうな感じがする。

1学年6クラスくらいあったとしたら6年生までで36組。

同じ誕生日の子がクラスにいるクラスが18クラスくらいあってもおかしくないわけで。

そういえば、高校の時、自分と1日違いの子が2人いたなー。

あれって別に珍しい事でも何でもなかったんですね。。。

ランダムな値で重複しなさそうーと思ってても、かぶる事って結構あるよね。

色々作ってたり使ってたりする中で、確かにそんな状況に出会う事はあったような気がする。

確率的な数値って、直観と反する事ってありますよね。

例えば、ドロップ率1000分の1の場合で、

実際に1000回倒して最低でも一つ出る確率は約63%だったり。

結構出ない。。。

↑の話って数式で考えると

(1 - 1/n)のn乗じゃないですか。

この形って↓じゃん。

sample

↑のnが大きくなればなるほど、「0.36」辺りに収束すると。

↑のeってネイピア数っていうんですが、

だから、確率関連の数式には良く出てくるのかーって

意味も分からず納得してた記憶がある。

だから何だって話なんですが。

Python ソート色々(オブジェクト、辞書のリストのソート)

公式のソートHowTo

色々と。

単一項目でのソートはまー良いんだけど、

複数項目で、昇順/降順を個別指定してのソートは

サンプルとかあまり見なかったので↓の感じにした。

simple_list = [5,10,1,4,8,0]
tuple_list = [(3,'b'),(10,'x'),(5,'z')]

class TestItem:
    def __init__(self, **kwargs):
        self.id = kwargs["id"]
        self.name = kwargs["name"]
        self.val = kwargs["val"]
    def __repr__(self):
        return repr((self.id, self.name, self.val))

obj_list = []
obj_list.append(TestItem(id=10,name="y",val=1024))
obj_list.append(TestItem(id=1,name="z",val=256))
obj_list.append(TestItem(id=5,name="x",val=128))
obj_list.append(TestItem(id=101,name="y",val=512))
obj_list.append(TestItem(id=102,name="y",val=64))

dict_list = []
dict_list.append({"id":10, "name":"y", "val":1024})
dict_list.append({"id":1, "name":"z", "val":256})
dict_list.append({"id":5, "name":"x", "val":128})
dict_list.append({"id":101, "name":"y", "val":512})
dict_list.append({"id":102, "name":"y", "val":64})


from operator import attrgetter


if __name__ == "__main__":
    # 普通にソート
    print(sorted(simple_list))
    # → [0, 1, 4, 5, 8, 10]

    # 降順にソート
    print(sorted(simple_list, reverse=True))
    # → [10, 8, 5, 4, 1, 0]

    # タプルの配列のソート(タプルの最初の項目でソート)
    print(sorted(tuple_list))
    # → [(3, 'b'), (5, 'z'), (10, 'x')]

    # タプルの配列の降順でソート(タプルの指定の項目でソート)
    print(sorted(tuple_list, key=lambda x: x[1], reverse=True))
    # → [(5, 'z'), (10, 'x'), (3, 'b')]

    # オブジェクトの配列のソート
    print(sorted(obj_list, key=lambda x: x.name))
    # → [(5, 'x', 128), (10, 'y', 1024), (101, 'y', 512), (102, 'y', 64), (1, 'z', 256)]
    
    # オブジェクトの配列のソート(attrgetter使用)
    print(sorted(obj_list, key=attrgetter("name")))
    # → [(5, 'x', 128), (10, 'y', 1024), (101, 'y', 512), (102, 'y', 64), (1, 'z', 256)]

    # 辞書の配列のソート
    print(sorted(dict_list, key=lambda x: x["name"]))
    # → [{'id': 5, 'name': 'x', 'val': 128}, {'id': 10, 'name': 'y', 'val': 1024}, {'id': 101, 'name': 'y', 'val': 512}, {'id': 102, 'name': 'y', 'val': 64}, {'id': 1, 'name': 'z', 'val': 256}]

    # ここから複合ソート

    # 複合ソート用関数
    def multisort(xs, specs, fkey):
        for key, reverse in reversed(specs):
            xs.sort(key=fkey(key), reverse=reverse)
        return xs
    
    # key関数を作る高階関数(オブジェクト用)
    def fobj(key):
        return attrgetter(key)

    # key関数を作る高階関数(dict用)
    def fdict(key):
        return lambda x: x[key]

    # オブジェクトの配列の複合ソート
    print(multisort(obj_list, [('name', False), ('val', False)], fobj))
    # → [(5, 'x', 128), (102, 'y', 64), (101, 'y', 512), (10, 'y', 1024), (1, 'z', 256)]

    # 辞書の配列の複合ソート
    print(multisort(dict_list, [('name', False), ('val', False)], fdict))
    # → [{'id': 5, 'name': 'x', 'val': 128}, {'id': 102, 'name': 'y', 'val': 64}, {'id': 101, 'name': 'y', 'val': 512}, {'id': 10, 'name': 'y', 'val': 1024}, {'id': 1, 'name': 'z', 'val': 256}]

で、Pythonのソートは安定(同一値の場合に元の並び順のままになる)なのを

保証してるみたいなので、上のロジックで複数キーのソートが出来るとさ。。。

MVCでトランザクションをどこに持たせるか

今更MVCの話。

結局MVCって役割の分離って事じゃないですか。

WebアプリでMVC(厳密に言うと原書のMVCではないけども)っぽく作る場合。

色んなサンプルを見てるとMVCのCにトランザクションのBeginとEndがあったりする。

システムのアーキテクチャ上、コントローラでトランザクション制御するっていうモノももちろんあると思うけど、

それしかないのもどうかなと思うのと、微妙な気がしてるので少し書いておく。

まず、Contorlerとは何をするべきなのかって事。

Webアプリだと、ココはやっぱり何のビジネスロジックを呼び出すかっていう部分と、

認証周りの処理、セッション周りの処理、あとはリクエストの内容を解析して、

ビジネスロジックに渡すイベントオブジェクト的なものを作る処理辺りだと思うのです。

で、良くあるトランザクションの処理で言われてるところが、

AっていうDBを更新する処理があって、その後にBっていう他のデータを更新する処理があって、

その2つの処理を関連付けて一つのトランザクションとして扱いたい

みたいなものがある。

その制御をするためにはモデルを呼ぶコントローラでやらなきゃ!

って感じなのかな~と。

そこに違和感を感じるわけです。

処理を関連付けるとか処理する順番に依存してるとかとか。

それに意味があるなら、それはビジネスロジックだよね?

という事でトランザクションの制御はコントローラではやらない方がいいと思うのです。

Contorlerに入れた時点で、HTTPのリクエスト渡さないと他から使えないじゃない?

モデル側でファサード的なクラスを用意しておけばいいように思うわけです。

MVCってModelViewContorlerに分ける「方向性」で組むと思うんですが、

分割の仕方は3つである必要はないと思うんですよ。

大事なのは役割の分割であって、3つという数に意味はないように思うのです。

なので、Controlerに持たせるのは何か微妙に思う。

あと、結構致命的だと思ってるのは、テスト書き辛くない?

Webアプリのコントローラって認証とセッションにほぼ結合してると思うので、

その辺の調整をテストコード側でやらないと呼ぶの無理じゃん?

テスト書きヅラ!!みたいな。

という事で、MVCを元に構成したWebアプリでトランザクション処理する時は

コントローラでやるべきではないと思うのでした。。。

Python typeでクラス定義を書き換える

なんのこっちゃって感じ。rest_frameworkのソース読んでて、

あー、これはこういう事に使うのかってピンと来たので残しておく。

DjangoのModelもそうなんだけど、シリアライザを作る時に

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

こんな感じで、「クラス変数」にモデルの定義を入れてる。

で、クラス変数ってstaticだから、インスタンス化して使おうが、

結局全インスタンスで共有されてる変数。

でも、DjangoもRestFrameworkもちゃんとインスタンス変数になってる。

なんでかなーって不思議だったんですが、ソース読んでると、

metaclassの定義使って、クラス定義読み込まれる時に、クラス変数を潰して、

インスタンス変数作ってるんですね。

RestFrameworkのソースだと↓の部分。

class SerializerMetaclass(type):
    """
    This metaclass sets a dictionary named `_declared_fields` on the class.
    Any instances of `Field` included as attributes on either the class
    or on any of its superclasses will be include in the
    `_declared_fields` dictionary.
    """

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)

        # If this class is subclassing another Serializer, add that Serializer's
        # fields.  Note that we loop over the bases in *reverse*. This is necessary
        # in order to maintain the correct order of fields.
        for base in reversed(bases):
            if hasattr(base, '_declared_fields'):
                fields = [
                    (field_name, obj) for field_name, obj
                    in base._declared_fields.items()
                    if field_name not in attrs
                ] + fields

        return OrderedDict(fields)

    def __new__(cls, name, bases, attrs):
        attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
        return super().__new__(cls, name, bases, attrs)

で、Serializerの定義が↓

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):

SerializerMetaclassの↓のところ

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)

attrsにクラス定義でのクラス変数とか入ってて、これが最終的にクラス定義内に

展開されるっぽい。なので、attrs.popでクラス変数として消してる。

消すタイミングで、fieldsって変数に定義を退避してる感じ。

で、typeを継承してて、最後に「return super().__new__(cls, name, bases, attrs)」してる

という感じ。

なるほどー。ってなんか納得した。

pythonでメタプログラミングするならtype継承してクラス定義が作られてるって

知っとけみたいな話見てて「は?」って思ってたんだけど、

こう具体的なtypeの使い方見ると納得できますね。。。

で、↑のが何で便利なのかというと、MVCとMVVMとかで作ってる時って、

View層とのやり取りは専用のViewModel作ると思うんですよ。

バリデーションとか統一しやすいし。

で、ViewModelの定義する時のベースにこれがすごく使える。

一つ一つのフィールドのデータ型を個別に作れるので。

一つ賢くなりましたというお話。。。

clocでgitのブランチ間の修正行数とかをカウントする

gitで管理してるリポジトリのコード行数とか色々集計したくなった。

コミットの追加行数とか修正行数は見ればわかるんだけど、

色々集計しようとしたりすると、gitのみだと割と使えない。

なのでclocっていうツールを使ってやる。

って話が割と見るんだけど、結構大変だったのと、clocってgitのリポジトリ集計するとき

だいぶイケてないように思うのでちょっと書いておく。

あ、Windowsでやります。

まず、インストール。現状の最新は1.82。

clocのgithubから

で、ググって出てくるサイトが最初はコッチ

古い方のサイトなので、注意。GitHubのreleaseから取る。

DLしてとりあえず動かそうとすると「unzipが無い」みたいなエラーが出る。

なので、unzipをDL。

unzipの公式

このunzipのインストールが必要ってclocの公式に書いて無いような気がするんだけども。。。

で、clocとunzipともpathを通しておかないとまずいっぽいので、path通す。

そしたら、対象のリポジトリのディレクトリに行って下の感じ。

git rev-list --max-parents=0 HEAD
>> ae5d586ed47385ded893d95c3ac66724e41259c4

cloc --diff ae5d586ed47385ded893d95c3ac66724e41259c4 HEAD

これで集計はされるんですが、割と↓のエラーが出るんでないかと。

fatal: pathspec ''Program.cs'' did not match any files
Failed to create tarfile of files from git. at script/cloc line 4584.

何でこれが出るのか全然分からなくて、ソース見てみたんですが、

gitのリポジトリからやる場合、下記の流れてやってるっぽい。

それぞれのハッシュから変更のあったファイル名を

git ls-tree --name-only

で持ってきて、

git archive

にファイルの一覧を渡してZIPなんかにして、

そのZIPファイルをclocで集計するみたいな動きになってる。

で、どうも比較するハッシュのどっちかに無いファイルがあると、

archiveが失敗してるって事みたい。

ダメじゃん。。。

cloc自体、スター結構ついてるし使ってる人いっぱいいると思うのですが、

どうみてもコレ出来ないんですよ。

どーなんだろ、みんな困ってないのかしら?

cloc自体、生のファイルかZIPとかのアーカイブからじゃないと集計できないって事なのね。

そしたら、自分でgit の zipアーカイブをいい感じに作るしかないですね。

とりあえず、ブランチ間の差分をとるなら下の感じでやれる。

git archive test --output=test.zip

git archive master --output=master.zip

cloc --diff test.zip master.zip

変更されたファイルのみやりたい場合は、なんかスクリプト組むか

プログラム作るしかなさそうですね。。。

C#でちょっと「ほや?」って思った事

大した話じゃないんですが、下記ソース。

public void hoge()
{
    //プリミティブ
    int t = 1;
    int tt = t;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(t, tt));//⇒False
    //オブジェクト
    Object o = new object();
    Object oo = o;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(o, oo));//⇒True
    //string
    string s = "s";
    string ss = s;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(s, ss));//⇒True
    //オブジェクトの中のプリミティブ
    SampleValue x = new SampleValue();
    SampleValue xx = x;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(x, xx));//⇒True
    Console.WriteLine("ref={0}", Object.ReferenceEquals(x.v, xx.v));//⇒False!!!
}
public class SampleValue
{
    public int v = 1;
}

オブジェクトは参照渡しだから、ReferenceEqualsがTrueになるのは分かるんだけど、

中のプリミティブな奴ってFalseになるんですね。

そうなのかーーーと思って。

なんか、すんませんでした。。。

DjangoとCeleryのジョブキューについて

Pythonでジョブキュー的に処理を外に投げたい場合に

Django+Celeryが良く見るやつ。

公式の通りにやれば特に問題なく動く。

で、一つ気になったところ。

これって、Djangoのアプリの中で下の感じで処理を投げるじゃないですか。

def aaction(request):
    res = some_task.delay()
    return HttpResponse(f"Delay Task!!!")

そしたら、Workerサーバというかサービスが↑でジョブキューに入った

処理依頼を処理する流れじゃないですか。

で、その時のWorkerプロセスの動きが、どう見ても、毎回Djangoアプリを初期起動してるんだよね。

そういうもん?バッチ処理的に使うなら、起動時間は全体の処理時間からみて

問題にならないって事なんかな?

2~3秒かかる程度の処理は投げない方が良い感じ?

それなりに大きいアプリだと起動処理もそこそこオーバーヘッドあると思うんだけど。。。

多分中で、django.setup()してると思うんだけど、

どうなんでしょ。何となく、起動時にsetup()はやって使いまわして欲しいのですが。。。

つか、それやったら、Djnagoの普通のWebサーバだろみたいな感じかしらね。

何とも釈然としないモヤモヤ感。

何かいいやり方ないかなー。もうちょい検討。。。

問合せ